Javaの同期制御において、synchronizedキーワードとwait()/notify()メソッドの組み合わせは古典的な手法ですが、ReentrantLockとConditionインタフェースの組み合わせを使うことでより柔軟な制御が可能です。Conditionオブジェクトを複数生成することで、特定の条件に応じたスレッド通知が可能になります。
標準のsynchronized構文では単一の待機領域しか持たないのに対し、ReentrantLockでは複数のConditionインスタンスを生成できます。これにより、特定のConditionに関連付けられたスレッドだけを選択的に起床させることが可能です。
基本的なロック使用例
public class SequentialPrinter implements Runnable {
private static final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.lock();
for (int count = 0; count < 5; count++) {
System.out.println(Thread.currentThread().getName() + ": " + count);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
SequentialPrinter printer = new SequentialPrinter();
new Thread(printer, "Thread-A").start();
new Thread(printer, "Thread-B").start();
new Thread(printer, "Thread-C").start();
}
}
Conditionを使用した待機/通知例
public class ConditionalNotifier {
private static final ReentrantLock sharedLock = new ReentrantLock();
private static final Condition triggerCondition = sharedLock.newCondition();
public static void main(String[] args) {
class Waiter implements Runnable {
@Override
public void run() {
sharedLock.lock();
try {
System.out.println("待機スレッド開始");
triggerCondition.await();
System.out.println("待機スレッド終了");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
sharedLock.unlock();
}
}
}
class Signaler implements Runnable {
@Override
public void run() {
sharedLock.lock();
try {
System.out.println("通知スレッド開始");
triggerCondition.signal();
System.out.println("通知スレッド終了");
} finally {
sharedLock.unlock();
}
}
}
new Thread(new Waiter(), "Waiter-1").start();
new Thread(new Signaler(), "Signaler-1").start();
}
}
Conditionのawait()メソッド呼び出し後、ロックは解放され、対応するsignal()メソッド呼び出しで再取得可能になります。このパターンにより、複数の待機条件を個別に管理できるようになります。