いいえ、同期は、どちらThreadが最初に到達するかについて保証しません。Thread一度に複数の同期ブロックにアクセスできないことを保証するだけです。
synchronizedブロックは、ドアに鍵がかかっている部屋と考えてください。誰が最初にドアにたどり着くかはわかりませんが、ドアに入るとドアに鍵をかけます。
他の人は、部屋にいる人が部屋を出る準備ができてドアのロックを解除するまで待たなければなりません。それが起こると、誰もが再びドアに入るために競争します.
それはすべて、s をオフにする順序によって異なりますThread。ただし、JVM が と をランダムにサスペンドする可能性があるため、保証はありませThreadん。例えるなら、先頭に立ってドアにたどり着く人がバナナの皮につまずくかもしれません。可能性は低いですが、常に可能です。Threadyield
保証が必要な場合は、wait/ notify- を実行する必要があります。これにより、読み取りスレッドが をチェックしflag、 である場合falseはループ内で一時停止します。
次に、書き込みスレッドは を設定しflag、読み取りスレッドを起動するロック モニターに通知します。
LockJava 5 のand APIを使用した例を次に示します。and /よりもそれを使用する必要がConditionあると言っているわけではありません。synchronizedwaitnotify
はReaderを取得し、ループでフラグをlockチェックします。readyフラグがオンの場合はfalseオンawaitです。これにより、 がアトミックに解放され、 がlock中断されThreadます。
一方、Writerは を取得し、およびフラグlockを設定します。次に、 を呼び出して解放します。datareadysignalAlllock
これによりが起動し、 as がReader読み取られ、ステートメントに進みます。readytrueprint
出力は常に42(never -1) になります。
public class App {
static volatile boolean ready = false;
static volatile int data = -1;
private static class Reader implements Runnable {
private final Lock lock;
private final Condition condition;
public Reader(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
while (!ready) {
try {
condition.await();
} catch (InterruptedException ex) {
//oh well
}
}
System.out.println(data);
} finally {
lock.unlock();
}
}
}
private static class Writer implements Runnable {
private final Lock lock;
private final Condition condition;
public Writer(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
data = 42;
ready = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(2);
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
executorService.execute(new Reader(lock, condition));
executorService.execute(new Writer(lock, condition));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
}
}