簡単な解決策は、必要なすべての待機操作と通知操作を実行するjava.util.concurrent.locks.Lockを使用することです。
public class ThreadTest {
private static final Lock lock = new ReentrantLock();
public static final void main(String[] args) {
Runnable aRun;
Runnable bRun;
aRun = new Runnable() {
public void run() {
while (true) {
lock.lock();
System.out.println("a");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
lock.unlock();
}
}
};
bRun = new Runnable() {
public void run() {
while (true) {
lock.lock();
System.out.println("b");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
lock.unlock();
}
}
};
Thread aThread = new Thread(aRun);
Thread bThread = new Thread(bRun);
aThread.start();
bThread.start();
}
}
モニターを使用しなくても、このように実行できますが、@noahz が謙虚に指摘しているように、効率的ではないスピンロックを使用します。
public class ThreadTest {
private static volatile Boolean isATurn = true;
public static void main(String[] args) {
Runnable aRun;
Runnable bRun;
aRun = new Runnable() {
public void run() {
while (true) {
while (!isATurn) {
}
System.out.println("a");
isATurn = false;
}
}
};
bRun = new Runnable() {
public void run() {
while (true) {
while (isATurn) {
}
System.out.println("b");
isATurn = true;
}
}
};
Thread aThread = new Thread(aRun);
Thread bThread = new Thread(bRun);
aThread.start();
bThread.start();
}
}
私が知る限り、これによりデッドロックが発生しないことが保証されますが、スレッドの 1 つが終了しないと、他のスレッドが待機しているため、飢餓が発生する可能性があります。ただし、このような単純な例では問題になりません。モニターもポーリングを使用するよりも優先されますが、少し複雑になります。