スレッドが交代でプリントするJavaの仕組み
synchronized キーワードと wait()、notify() メソッドを用いて、2 つのスレッドによる交互の出力を行う
以下は簡単な例です。Aスレッドは奇数を、Bスレッドは偶数をプリントします。
public class AlternatePrint {
private int count = 1;
private final Object lock = new Object();
public static void main(String[] args) {
AlternatePrint alternatePrint = new AlternatePrint();
Thread threadA = new Thread(() -> alternatePrint.printOdd());
Thread threadB = new Thread(() -> alternatePrint.printEven());
threadA.start();
threadB.start();
}
public void printOdd() {
while (count <= 100) {
synchronized (lock) {
if (count % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ": " + count);
count++;
lock.notify(); // 唤醒等待的线程
} else {
try {
lock.wait(); // 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void printEven() {
while (count <= 100) {
synchronized (lock) {
if (count % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + count);
count++;
lock.notify(); // 唤醒等待的线程
} else {
try {
lock.wait(); // 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
上記例では、共有ロックオブジェクトlockをロックとして用い、各スレッドはsynchronized(lock)でロックオブジェクトを取得します。countが奇数のときはAスレッドがcountの表示と加算を行い、その後lock.notify()メソッドを呼び出して待機状態にあるBスレッドを待ち起こします。countが偶数のときはBスレッドがcountの表示と加算を行い、その後lock.notify()メソッドを呼び出して待機状態にあるAスレッドを待ち起こします。countの値が現在スレッドの表示条件に合致しない場合、現在スレッドはlock.wait()メソッドを呼び出すことで待機状態に移り、呼び起こされるまで待ちます。
ロック.wait() とロック.notify() メソッドは synchronized ブロック内でしか呼べません。呼び出さないと IllegalMonitorStateException 例外が発生する点に注意してください。また、スレッドの開始順序は確定していないので、A と B スレッドの印字順序が異なる可能性があります。