【发布时间】:2013-02-08 05:47:48
【问题描述】:
我根据this page上的例子做了一个线程池。 在工作线程中,我们有一个永远不会让线程死亡的无限循环,以及当没有工作要做时暂停线程的 wait() 方法调用:
while (true) {
synchronized(queue) {
loop:while (queue.isEmpty()) { // labled the loop so we can return here
try
{
queue.wait();
if(queue.isEmpty()) // check the condition predicate again
continue loop;
}
catch (InterruptedException ignored)
{
}
}
r = (Runnable) queue.removeFirst();
}
// If we don't catch RuntimeException,
// the pool could leak threads
try {
r.run();
}
catch (RuntimeException e) {
// You might want to log something here
}
事实上,如果队列为空,r = (Runnable) queue.removeFirst(); 可以抛出 NoSuchElementException,这是一个 RuntimeException。并且当在该行上抛出这样的异常时,持有互斥锁的当前线程死亡并且池泄漏线程。当线程死亡时,互斥锁似乎被释放了。
但是,如果您不使用默认的synchronized 关键字来同步队列,而是使用ReentrantLock 锁定并使用Condition 发出信号和等待,则持有互斥锁的当前线程似乎不会释放意外中断时锁定。
因此,就我而言,当我在“线程”选项卡下检查 JVisualVM 时,我可以看到 AWT-EventQueue-0 线程正在等待 Thread-1 释放互斥锁。但是 Thread-1 在运行任务的途中死了,意外终止了 (RuntumeException),而且互斥锁似乎没有被释放。
我的问题:
1) 如果持有它的线程意外终止,ReentrantLocks 不会被释放吗?
2) 上面的sn-p代码中while (queue.isEmpty()) {和if (queue.isEmpty()) {有什么区别吗?我看不出有什么区别,因为线程在这两种情况下都会等待。但我认为使用if 时的行为会有所不同(例如,如果多个线程会影响队列)。
编辑
Java Concurrency in Practice 状态:
由于所有这些原因,当你从等待中醒来时,你必须 再次测试条件谓词,然后返回等待 (或失败)如果它还不是真的。既然你可以醒来 在您的条件谓词不为真的情况下反复向上,您 因此必须始终从循环中调用等待,测试 每次迭代中的条件谓词。
看看我在上面代码中的编辑,现在代码应该是正确的,如 Java Concurrency in Practice 中所述。
【问题讨论】:
-
关于代码和忽略 InterruptedException 的注释。你打算如何停止你的申请?你使用 System.exit() 吗?
-
第二次检查是否不需要if语句中的队列为空。您将在下一次迭代之前检查相同的条件
-
注意:如果你希望你的线程可以被中断,请使用 lockInterruptibly()(docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/…) 而不是普通的 lock()。
标签: java multithreading synchronized reentrantlock