常识,之前有的没搞清楚,导致写代码时不少疑惑。
比较典型的同步例子,用了两个Condition,notEmpty和notFull,分别对应两个lock,takeLock和putLock。查看take的代码:
E x;
int c = -1;
final AtomicInteger count = this.count;
final AtomicInteger takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
try {
while (count.get() == 0) notEmpty.await();
} catch (InterruptedException ie) {
notEmpty.signal();
throw ie;
}
x = extract();
c = count.getAndDecrement();
if (c > 1) {
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity) {
signalNotFull();
}
return x;
singalNotFull中代码:
putLock.lock();
try { notFull.signal(); } finally { putLock.unlock(); }
可以看出:
1.take操作可能会block,应允许终端,所以用lockInterruptably更友好
2.for循环await是java文档推荐的,await成功不代表条件满足,这种唤醒称为伪唤醒(spurious wakeup)
3.await()抛出InterruptedException后signal让下一个等待线程伪唤醒
4.用signal而不是signalAll,一次只唤醒一个线程,取完之后发现还有就再signal,唤醒下一个等待线程,这是因为condition可用时不一定能让所有take线程获取成功(看队列当前size够不够),因而不必signalAll
5.使用了两把锁,take时notEmpty(lock await)=>notFull(lock signal),put时:notFull(lock await) => notEmpty(lock signal),因此take和put可以兵法,另外有个问题是put在notFull lock后进入await,block住了,take线程会不会拿不到notFull的锁signal而导致死锁呢?答案是不会,因为await会先释放掉锁然后在返回钱重新获得锁。其实object的wait也有相同特性。
原文地址:http://blog.csdn.net/jollyjumper/article/details/39433429