之前总结过,线程的暂停只有两种可能,等待锁(lock.lock(), synchronized(lock))和等待条件(condition.await(), object.wait())
其他的synchronizer,比如CountDownLatch, Barrier,BlockingQueue等,都是基于等待条件的,可以用condition实现。而condition又以锁为基础,condition是共享数据,访问(check 和update)必须在锁里。
问题来了,锁本身其实又是基于某种condition, 就是进入临界区的线程数count,当count == 0可以进入,否则不能进入。这个condition如何保护?可以假想一下可能的实现
1)CAS + 自旋:
2)CAS + 等待
读写锁也是一种条件:是否有写线程在等待,writeCount > 0, 这个条件为真前,都可以进入,同时,读线程不更新这个条件,写线程更新条件。
下面是一个读写锁的模拟实现,可以看到同步器的三要素
1)条件/同步状态:线程是否可以继续的依据(readCount,writeCount)
2)锁:保护同步状态的读写(ReentrantLock lock)
3)信号:使得线程等待,或者唤醒信号上等待的线程(readSignal, writeSignal)
外面的try/finally是保证unlock被调用;里面的try/catch是为了保证等待不被interrupt,因为我们要实现的是锁,锁上的等待(比如synchronized块)一般是不可interrupt的,而等待条件是可以interrupt的,本例子是用条件的等待模拟实现锁。
class MyReadWriteLock {
ReentrantLock lock = new ReentrantLock();
Condition readSignal = lock.newCondition();
Condition writeSignal = lock.newCondition();
int writeCount = 0, readCount = 0;
class ReadLock implements Lock {
@Override
public void lock() {
try {
lock.lock();
while (writeCount > 0) {
try {
readSignal.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
++readCount;
}
finally {
lock.unlock();
}
}
@Override
public void unlock() {
try {
lock.lock();
if (readCount > 0) --readCount;
writeSignal.signalAll();
}
finally {
lock.unlock();
}
}
}
class WriteLock implements Lock {
@Override
public void lock(){
try {
lock.lock();
while (readCount > 0 || writeCount > 0 ) {
try {
writeSignal.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
++writeCount;
}
finally {
lock.unlock();
}
}
@Override
public void unlock() {
try {
lock.lock();
if (writeCount > 0) --writeCount;
readSignal.signalAll();
writeSignal.signalAll();
}
finally {
lock.unlock();
}
}
}
Lock readLock() {
return new ReadLock();
}
Lock WriteLock() {
return new WriteLock();
}
}