[英]Trouble understanding synchronized in java
大家好,我正在做一個簡單的程序來計算從 0 到 99 的數字總和。我正在閱讀 Java 中的線程,我試圖了解它是如何工作的,尤其是多線程,所以我寫了一個簡單的程序來理解這個概念。 但是我的程序的輸出是不同的,因為它輸出 0 和 4950。似乎有 2 個線程在運行? 主線程然后對象線程一個? 我的輸出有問題,因為它們不同步。 我希望我走在正確的軌道上,但我不確定,需要指導。 有人可以解釋一下如何使用同步來解決這個問題。 難以理解?
public class Testing {
public static void main(String[] args) {
ThreadB b = new ThreadB();
Thread a = new Thread(b);
a.start();
System.out.println(b.total);
}
}
class ThreadB extends Thread {
int total;
public ThreadB() {
this.total = 0;
}
public synchronized int total() {
for(int i = 0; i < 100; i++) {
total += i;
}
return total;
}
public void run() {
System.out.println(total());
}
}
您有兩個線程:主線程和線程“a”。 您有另一個對象 b,它繼承自 Thread,但您將其視為 int。
您運行線程 a,它執行求和並打印結果。 然后打印存儲的 b 中的值。
同步沒有問題,因為您的線程都沒有共享相同的變量。 a 和 b 之間的 int 總數是不同的。 如果要創建問題,請將 int 設為 Testing 的成員。
當兩個或多個線程試圖訪問同一個資源並且您想確保它們不會同時進行時,需要進行同步。 從寫入的角度來看,這通常很重要,但從讀取的角度來看則不那么重要(即:多個線程可以同時讀取一個值,只是在更新時不同,或者沒有多個線程更新相同的資源同時)。
在您的示例中,您實際上只有 1 個線程正在運行。 我不懷疑這是故意的。 有主線程和線程a
。 我在沒有更改任何內容的情況下重新組織了您的代碼,但希望這會更清楚:
public class Testing {
public static void main(String[] args) {
ThreadB b = new ThreadB();
System.out.println(b.total);
Thread a = new Thread(b);
a.start();
}
}
主線程實際上沒有做任何事情。 它只是創建一個新對象 b,並打印出 b.total (因此您得到 0 值)。 至少,您也應該調用b.start()
。
您啟動的計算線程在您調用a.start()
。 這部分在打印計算總數的 ThreadB() 對象中啟動計算。
如果你真的想測試並發性,那么你需要有一個多線程訪問的共享資源。 但是,在您的情況下,由於total
是私有實例成員,因此線程 a 和 b 都將具有不同的total
實例並且沒有任何同步問題。 synchronized
語句是多余的,因為每個線程都在調用它自己的total()
方法。
http://www.tutorialspoint.com/java/java_thread_synchronization.htm有一個很好的解釋/同步和多線程的例子。
需要明確的是,這里只有兩個線程。 JVM 提供了用於執行 main 方法的線程。 然后有一個名為a
的局部變量,它引用了一個 Thread 對象。 局部變量b
指的是一個線程,但它不用作線程,因為它從未開始調用它。 所有線程實現Runnable,通過b
為主題的構造a
是手段b
被用來作為一個Runnable a
。 所以線程a
運行,執行b
定義的 run 方法。
當a
執行b
的 run 方法時, b
上的鎖是無競爭的。 程序中沒有其他任何東西試圖獲取b
上的鎖。 total() 上的synchronized
關鍵字只影響調用 total() 方法的線程,因此沒有什么可以阻止 main 方法訪問名為 total 的b
的實例成員。 因此,main 方法繼續訪問 total,此時尚未更新,因為線程a
仍在啟動中。
如果您完全私有,並像這樣為 ThreadB 添加了一個訪問器:
public synchronized int getTotal() { return total;}
然后調用這個 getter 將強制調用者獲取鎖(因為這里的同步意味着“使用調用此方法的實例作為鎖”,因此 total() 方法和新的 getTotal() 方法將共享相同的鎖) 在獲得總價值之前。 然后,一旦線程a
進入其 run 方法,它將獲取b
上的鎖,並且主線程必須等到它可以獲取b
上的鎖才能訪問 total。
即使你做到這一點,但是,很可能在主線程仍然能夠訪問b
和獲取鎖之前線程a
可以上手,並采集鎖定b
。 主線程和線程a
之間存在競爭條件,主線程具有優勢,因為它已經啟動並處於活動狀態(而啟動新線程的開銷很大)。 在獲取總數之前,您可以在 main 方法中添加一個 Thread.sleep,以便b
線程有機會先獲取鎖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.