簡體   English   中英

為什么算得少甚至使用synchronized關鍵字?

[英]Why count less even used synchronized keyword?

我開始學習一些使用的java並發概念。 但是這段代碼之一超出了我的理解范圍。

public class Count { 
    private int count = 0; 
    public synchronized void setCount(int count) { 
        this.count = count; 
    } 
    public synchronized int getCount() { 
        return count; 
    } 
} 
class CountRunner extends Thread { 
    Count count; 
    public CountRunner(Count count) { 
        this.count = count; 
    } 
    public void run() { 
        for (int i = 1; i <= 1000; i++) { 
            count.setCount(count.getCount() + 1); 
        } 
    } 
} 
class TestCount { 
    public static void main(String[] args) throws Exception { 
        Count count = new Count(); 
        CountRunner runnerA = new CountRunner(count); 
        CountRunner runnerB = new CountRunner(count); 
        runnerA.start(); 
        runnerB.start();         
        runnerA.join(); //join statement here 
        runnerB.join(); 
        System.out.println("count.getCount = " + count.getCount()); 
    } 
} 
Question:
1. The result is a little less than 2000 for many times, why ?
2. if delete 2 join() statement, why count.getCount = 451,even less ?
3. i think there will be no effect of deleting the join() statements,
because i already have Synchronized method to lock one object to one thread each time ? 
So, What's the point of using Synchronized and join() ?

這很簡單。 通過調用getCount + 1來調用setCount方法。在輸入方法之前,運行時會計算getCount(synchronized),但是在離開getCount並輸入setCount並且其他線程可以輸入調用getCount時,您不會保持鎖定。 因此,每隔兩次(或更多,取決於您創建的線程數)線程將在getCount中具有相同的值。 想象一下,線程A在getCount中輸入並接收值1。 運行時產生它執行到胎面B,它調用getCount並接收相同的值1.線程B將值設置為1並再次運行50次,因此在該階段您的計數將為50。 運行時產生執行到線程A,線程A調用setCount為1(記住它沒有設法調用setCount並產生它的exec)。 現在A將值設置為1(這是錯誤的)。

改變你運行這樣的實現:

public void run() { 
    for (int i = 1; i <= 1000; i++) {
      synchronized(count){ 
        count.setCount(count.getCount() + 1); 
      }
    } 
} 
  1. 如果你打破了界限

    count.setCount(count.getCount()+ 1);

分成3行,它會更清晰:

final int oldCount = count.getCount(); // a
final int newCount = oldCount + 1; // b
count.setCount(newCount); // c

請注意,雖然語句(a)和(c)各自同步,但整個塊 同步。 因此它們仍然可以交錯,這意味着線程A可以在線程B執行線程(a)之后但它完成/進入語句(c) 之前進入/執行語句(a)。 當發生這種情況時,線程(a)和(b)將具有相同的oldCount並因此錯過一個增量。

2。

join()用於確保在打印之前線程A和線程B都完成。 你得到一個較小的數量的原因,因為當你打印你的結果線程A和B可能還沒有完成運行。 換句話說,即使你沒有join()完美地同步,你仍然會有一個遠小於2000的數字。

3.見2的回答。

1)因為你沒有正確鎖定。 你正在調用getCount() ,它會鎖定,獲取值並解鎖,遞增並調用setCount()來鎖定,保存值和解鎖。 在某些情況下會發生的情況是,兩個線程調用getCount() ,第一個胎面鎖定,獲取值x並解鎖。 然后第二個線程獲得鎖定,獲得相同的值x並解鎖。 由於兩個線程都會遞增並稍后保存相同的值,因此您將獲得比預期更低的計數。

2)如果沒有join()你將不會等待你的線程完成,你的主線程將只調用getCount()並在線程仍在運行時獲得一個隨機值。

3)由於其它線程不擁有鎖所有的時間 (如果你想他們都在運行,他們需要彼此解鎖時給予畢竟),您將需要join()仍。

someThread.join()將導致調用Thread等待someThread完成。

如果刪除join()調用,那么主Thread可能會在計數完成之前調用getCount() ,因為someThread可能仍在運行。

一個同步方法僅僅意味着一個以上的同步呼叫Object不能在同一時間進行。

一行答案是count.setCount(count.getCount() + 1); 不是原子操作。

或者稍微小一些,而setCountgetCount是正確的線程安全和原子的,沒有什么能阻止另一個線程這個線程調用setCountgetCount 之間調用這些方法中的任何一個。 這將導致計數迷失。

避免計數丟失的一種方法是創建原子increment()操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM