![](/img/trans.png)
[英]Why this is not right by using synchronized (Java concurrency) in this example?
[英]Concurrency in Java using synchronized blocks not giving expected results
下面是一個簡單的java程序。 它有一個名為“cnt”的計數器,它會遞增,然后添加到名為“monitor”的List中。 “cnt”由多個線程遞增,並且值被多個線程添加到“監視器”。
在方法“go()”的末尾,cnt和monitor.size()應該具有相同的值,但它們不具有相同的值。 monitor.size()確實有正確的值。
如果通過取消注釋其中一個已注釋的同步塊來更改代碼,並注釋掉當前未注釋的塊,則代碼會生成預期結果。 此外,如果將線程計數(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");
}
}
好的,讓我們來看看你提到的不同可能性:
1。
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所做的)將確保塊內的代碼一次只能由一個線程執行。 因此,外部的2內部同步不是必需的,無論如何代碼都受到保護。
2。
for (int ii=0; ii<NUM_EVENTS; ++ii) {
synchronized( monitor) {
monitor.add(cnt);
}
synchronized(cnt) {
cnt++; // <-- why does moving the synchronized block here result in cnt being wrong?
}
}
好的,這個有點棘手。 cnt是一個Integer對象,Java不允許修改Integer對象(整數是不可變的),即使代碼表明這是在這里發生的事情。 但實際上會發生的是cnt ++將創建一個值為cnt + 1並覆蓋cnt的新Integer。 這就是代碼實際執行的操作:
synchronized(cnt) {
Integer tmp = new Integer(cnt + 1);
cnt = tmp;
}
問題是,一個線程將創建一個新的cnt對象,而所有其他線程都在等待鎖定舊的cnt對象。 線程現在釋放舊的cnt,然后嘗試獲取新cnt對象的鎖定並獲取它,而另一個線程獲取舊cnt對象的鎖定。 突然,2個線程處於臨界區,執行相同的代碼並導致競爭條件。 這是錯誤結果的來源。
如果刪除第一個同步塊(帶監視器的塊),那么結果會更加錯誤,因為競爭的可能性會增加。
通常,您應該嘗試僅對最終變量使用synchronized來防止這種情況發生。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.