ken*_*y_k 13 java concurrency multithreading
我一直在玩我自己的版本,使用'if',似乎一切正常.当然,如果使用signalAll()而不是signal(),这将会崩溃,但如果一次只通知一个线程,那怎么会出错呢?
他们的代码在这里 - 检查put()和take()方法; 可以在JavaDoc for Condition的顶部看到更简单,更多点的实现.
我的实施的相关部分如下.
public Object get() {
lock.lock();
try {
if( items.size() < 1 )
hasItems.await();
Object poppedValue = items.getLast();
items.removeLast();
hasSpace.signal();
return poppedValue;
} catch (InterruptedException e) {
e.printStackTrace();
return null;
} finally {
lock.unlock();
}
}
public void put(Object item) {
lock.lock();
try {
if( items.size() >= capacity )
hasSpace.await();
items.addFirst(item);
hasItems.signal();
return;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
Run Code Online (Sandbox Code Playgroud)
PS我知道,一般来说,特别是在像这样的lib类中,应该让异常渗透.
Aff*_*ffe 20
防止虚假的唤醒.JVM无法保证线程再次开始运行的唯一可能原因是您按照预期的方式调用了信号.有时它会意外地开始并且去(Spurious wake up).因此,如果您要运行的条件实际上不正确,则必须再次等待.
这在wait方法的javadoc中有解释:http: //java.sun.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29
在等待的文档中提到:http: //java.sun.com/javase/6/docs/api/java/util/concurrent/locks/Condition.html#await%28%29
与此条件关联的锁被原子释放,并且当前线程因线程调度而被禁用,并处于休眠状态,直到发生以下四种情况之一:
一些其他线程为此Condition调用signal()方法,并且当前线程恰好被选为要被唤醒的线程; 要么
其他一些线程为此Condition调用signalAll()方法; 要么
其他一些线程会中断当前线程,并支持线程挂起中断; 要么
*发生"虚假唤醒".
Condition接口的某些实现可能会抑制虚假唤醒,但依赖于此将依赖于实现细节并使您的代码不可移植.
Gra*_*ray 14
为什么java.util.concurrent.ArrayBlockingQueue使用'while'循环而不是'if'来调用await()?
他们使用while而不是if防止生产者/消费者模型中的经典线程竞争条件,并防止更罕见的虚假唤醒情况.
当(例如)多个消费者正在等待特定条件(如队列为空)并且通知条件时,另一个线程可能首先锁定并"窃取"添加到队列中的项目.该while循环是线程在尝试将其排队之前确保队列具有项目所必需的.
我写了一些示例代码和更多文档来演示竞争条件.
查看您的具体代码,比赛如下:
await()while循环中,等待队列中的项目get(),锁定队列,并且必须等待#2解锁(它不等待,hasItems但它正在等待获取lock)hasItems.signal()以通知某人那里有一个项目if声明,它将继续前进,并试图从一个空的列表中出列,这个列表会抛出ArrayIndexOutOfBoundsException什么东西.while声明必要的原因是为了处理这些竞争条件.在上面的步骤8中,使用a while,线程#1将循环回到测试以查看队列中是否有项目,发现没有项目,然后返回等待.
这是一个经典的问题,绊倒了很多可重入的程序员.例如,O'Reilly pthreads圣经的初始版本有没有 while循环的示例代码,必须重新发布.
对于某些线程系统,系统更容易唤醒所有条件而不是已发出信号的特定条件,因此可能发生"虚假唤醒".该while循环对这种保护也是如此.