2012-08-06 84 views
8

如果此方法的變量'commonSet'是類級別字段,下面的代碼是否會導致相同的問題?如果它是一個類級別的字段,那麼我將不得不包裝添加到在同步塊內設置操作,因爲HashSet不是線程安全的。我應該在下面的代碼中做同樣的事情,因爲多個線程正在添加到集合中,甚至當前線程可能會繼續對集合進行變異。線程安全 - 最終本地方法變量傳遞給線程?

public void threadCreatorFunction(final String[] args) { 
    final Set<String> commonSet = new HashSet<String>(); 

    final Runnable runnable = new Runnable() { 
     @Override 
     public void run() { 
      while (true) { 
       commonSet.add(newValue()); 
      } 
     } 
    }; 

    new Thread(runnable, "T_A").start(); 
    new Thread(runnable, "T_B").start(); 
} 

對'commonSet'的引用是通過使用final來鎖定的。但是在它上面運行的多個線程仍然會破壞集合中的值(它可能包含重複項?)。其次,混淆是因爲'commonSet'是一個方法級變量 - 它的調用方法(threadCreatorFunction)和堆棧內存的運行方法的堆棧內存是相同的引用 - 這是正確的嗎?

有與此相關的不少問題:

但是,我看不到他們強調這種共享/傳遞mutables的線程安全的一部分。

+0

另請參閱http://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen – nos 2012-08-06 07:45:36

回答

9

不,這絕對不是線程安全的。只是因爲你已經把它放在最後一個變量中,這意味着兩個線程都會看到相同的參考,這很好 - 但它不會使對象變得更線程安全。

要麼你需要同步訪問,要麼使用ConcurrentSkipListSet

5

一個有趣的例子。

參考commonSet是線程安全和不可變的。它位於第一個線程的堆棧上,也是匿名Runnable類的一個字段。 (你可以在調試器中看到這個)

集合commonSet指的是可變的,而不是線程安全的。你需要使用synchronized,或者使用Lock來使線程安全。 (或者使用一個線程安全的集合,而不是)

1

我想你錯過了一個字,你的第一句話:

請問下面的代碼原因相同的問題,如果變量「commonSet」的這種方法是一個???而不是類級別字段。

雖然我覺得你有點困惑。併發問題與對可變數據結構的引用是否被聲明爲final無關。您需要將參考聲明爲final,因爲您的closing over它位於Runnable的匿名內部類聲明中。如果你實際上要有多個線程讀/寫數據結構,那麼你需要使用鎖(同步)或使用像java.util.concurrent.ConcurrentHashMap這樣的併發數據結構。

+0

更正第一行 – haps10 2012-08-06 07:53:44

+0

啊,我錯了 - 你有一個額外的詞! – DaoWen 2012-08-06 07:56:01

0

正如其他人已經評論過的,你錯誤地理解了一些概念,如final和synchronized。

我認爲,如果你解釋你想用你的代碼完成什麼,那麼幫助你會容易得多。我覺得這段代碼更像是一個實際代碼的例子。

一些問題:爲什麼在函數內部定義了集合?它應該在線程之間共享嗎?一些困惑我的是,你箱子兩個線程與可運行

new Thread(runnable, "T_A").start(); 
    new Thread(runnable, "T_B").start(); 
0

的相同實例是否commonset所使用單線程或多這僅僅是不可改變的最終對象(基準即分配一次你不能再次分配另一個obj引用),但仍然可以使用該引用修改此對象引用的內容。

如果不是最後一個線程可以再次初始化,並改變了參考 commonSet = new HashSet<String>(); commonSet.add(newValue()); 在這種情況下,這兩個線程可以使用兩個不同的commonsets這可能不是你想要

1

的commonSet是共享什麼兩個線程。您已將其聲明爲final,因此您使引用不可變(您不能重新分配它),但Set內的實際數據仍是可變的。假設一個線程放入一些數據,另一些線程讀取一些數據。每當第一個線程放入數據時,您最可能想要鎖定該Set,以便在寫入數據之前不會有其他線程讀取數據。這是否發生在HashSet?不是真的。