我根据此页面上的示例制作了一个线程池。在工作线程中,我们有一个永远不会让线程死亡的无限循环,以及当没有工作要做时暂停线程的 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();
可以抛出一个 RuntimeException 。NoSuchElementException
并且当在该行上引发此类异常时,当前持有互斥锁的线程将死亡,并且池会泄漏线程。当线程死亡时,互斥锁似乎被释放。
但是,如果您不使用 defaultsynchronized
关键字来同步队列,而是使用ReentrantLock
to 锁定和发送Condition
信号和等待,则当前持有互斥锁的线程在意外中断时似乎不会释放锁定。
因此,就我而言,当我在 Threads 选项卡下检查 JVisualVM 时,我可以看到AWT-EventQueue-0
线程正在等待Thread-1
释放互斥锁。但是 Thread-1 在运行任务的途中死了,并且意外终止(RuntumeException
),并且互斥锁似乎没有被释放。
我的问题:
1)如果持有它的线程意外终止,是否不会释放ReentrantLocks ?
2)上面的代码片段之间while (queue.isEmpty()) {
有什么区别吗?if (queue.isEmpty()) {
我看不出有什么区别,因为线程在这两种情况下都会等待。但我认为它在使用时的行为有所不同if
(比如如果多个线程会影响队列)。
EDIT
Java Concurrency in Practice 状态:
由于所有这些原因,当您从等待中醒来时,您必须再次测试条件谓词,如果尚未为真,则返回等待(或失败)。由于您可以在条件谓词不为真的情况下反复唤醒,因此您必须始终在循环内调用等待,在每次迭代中测试条件谓词。
看看我在上面的代码中的编辑,现在代码应该是正确的,如 Java Concurrency in Practice 中所述。