以下は簡単なJavaプログラムです。 "cnt"と呼ばれるカウンタがインクリメントされ、 "monitor"というリストに追加されます。 "cnt"は複数のスレッドで増分され、値は複数のスレッドによって "監視"に追加されます。同期ブロックを使用したJavaの並行処理で期待される結果が得られない
"go()"メソッドの最後では、cntとmonitor.size()は同じ値を持つ必要がありますが、そうではありません。 monitor.size()は正しい値を持ちます。
コメント付きの同期ブロックの1つのコメントを解除し、現在コメントを外しているコードをコメントアウトしてコードを変更すると、コードは予期した結果を生成します。また、スレッド数(THREAD_COUNT)を1に設定すると、コードは予期した結果を生成します。
これは、複数の実際のコアを持つマシンでのみ再生できます。
public class ThreadTester {
private List<Integer> monitor = new ArrayList<Integer>();
private Integer cnt = 0;
private static final int NUM_EVENTS = 2313;
private final int THREAD_COUNT = 13;
public ThreadTester() {
}
public void go() {
Runnable r = new Runnable() {
@Override
public void run() {
for (int ii=0; ii<NUM_EVENTS; ++ii) {
synchronized(monitor) {
synchronized(cnt) { // <-- is this synchronized necessary?
monitor.add(cnt);
}
// synchronized(cnt) {
// cnt++; // <-- why does moving the synchronized block to here result in the correct value for cnt?
// }
}
synchronized(cnt) {
cnt++; // <-- why does moving the synchronized block here result in cnt being wrong?
}
}
// synchronized(cnt) {
// cnt += NUM_EVENTS; // <-- moving the synchronized block here results in the correct value for cnt, no surprise
// }
}
};
Thread[] threads = new Thread[THREAD_COUNT];
for (int ii=0; ii<THREAD_COUNT; ++ii) {
threads[ii] = new Thread(r);
}
for (int ii=0; ii<THREAD_COUNT; ++ii) {
threads[ii].start();
}
for (int ii=0; ii<THREAD_COUNT; ++ii) {
try { threads[ii].join(); } catch (InterruptedException e) { }
}
System.out.println("Both values should be: " + NUM_EVENTS*THREAD_COUNT);
synchronized (monitor) {
System.out.println("monitor.size() " + monitor.size());
}
synchronized (cnt) {
System.out.println("cnt " + cnt);
}
}
public static void main(String[] args) {
ThreadTester t = new ThreadTester();
t.go();
System.out.println("DONE");
}
}
本当に何が起こっているのかは次のとおりです。 整数tmp =新しい整数(cnt + 1); は私が欠けていた部分でした。私は不変性とオートボケがこれにどのように影響するのかを考えていなかった。 – mangotang