このページの例に基づいてスレッドプールを作成しました。ワーカースレッドには、スレッドを停止させない無限ループと、実行する作業がないときにスレッドを一時停止する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
}
実際には、キューが空の場合、RuntimeExceptionであるr = (Runnable) queue.removeFirst();
をスローする可能性があります。NoSuchElementException
そして、そのような例外がその行でスローされると、ミューテックスを保持している現在のスレッドが停止し、プールがスレッドをリークします。スレッドが終了すると、ミューテックスが解放されるようです。
ただし、デフォルトのsynchronized
キーワードを使用してキューを同期する代わりに、を使用しReentrantLock
てロックしCondition
、シグナリングと待機を行う場合、ミューテックスを保持している現在のスレッドは、予期せず中断したときにロックを解放しないようです。
したがって、私の場合、[スレッド]タブでJVisualVMを確認すると、AWT-EventQueue-0
スレッドがThread-1
ミューテックスの解放を待っていることがわかりました。しかし、Thread-1はタスクを実行する途中で停止し、予期せず終了し(RuntumeException
)、ミューテックスは解放されなかったようです。
私の質問:
1) ReentrantLocksを保持しているスレッドが予期せず終了した場合、ReentrantLocksは解放されませんか?
2)上記のコードスニペットwhile (queue.isEmpty()) {
との間に違いはありますか?if (queue.isEmpty()) {
どちらの場合もスレッドは待機するため、違いはわかりません。ただし、使用時の動作は異なると思いますif
(複数のスレッドがキューに影響を与える可能性がある場合など)。
実際のJava同時実行の編集状態:
これらすべての理由から、待機から復帰するときは、条件述語を再度テストし、それがまだ真でない場合は待機に戻る(または失敗する)必要があります。条件述語がtrueでなくても繰り返しウェイクアップできるため、常にループ内からwaitを呼び出して、各反復で条件述語をテストする必要があります。
上記のコードでの私の編集を見てください。これで、コードはJava ConcurrencyinPracticeに記載されているとおりに正しくなるはずです。