2016-03-09 43 views
0

我有以下代碼:這段代碼是否線程安全,是否有更好的方法來實現它?

public class MySystem { 
    private ConcurrentHashMap<Integer, Integer> disabledList = new ConcurrentHashMap<>(); 

    public void toggle(int id) { 
     synchronized (disabledList) { 
      if (disabledList.containsKey(id)) { 
       disabledList.remove(id); 
      } else { 
       disabledList.put(id); 
      } 
     } 
    } 

    public boolean isInactive(int id) { 
     return disabledList.containsKey(id); 
    } 

} 

這可能是多個線程可以調用toggleId方法。上面的代碼是線程安全的嗎?有沒有更好的數據結構,我可以使用像CopyOnWriteArrayListConcurrentHashMap

理想情況下,我想擺脫同步塊,但我不知道是否有可能。

+1

哪個'ConcurrentHashSet'實現是你的代碼使用? –

+0

Erm ...調用你的類'System'有點不禮貌,因爲它會影響java.lang.System類。 –

+0

但我需要包裝檢查哈希集是否包含id和用於刪除syxinhronized塊中的id的代碼,否則在線程t2檢查包含後另一個線程(如t1)調用刪除時可能會出現競爭條件? – jcm

回答

0

這裏的問題是Java中的併發類不會鎖定自己來實現線程安全。因此,您不能在代碼中的某個位置依賴​​來與該對象的其餘訪問進行同步。

從Java文檔報價爲ConcurrentHashMap

不過,儘管所有操作都是線程安全的,檢索操作並不意味着鎖定,並沒有對的方式鎖定整個表中的任何支持阻止所有訪問。這個類可以在依賴線程安全性的程序中與Hashtable完全互操作,但不依賴於它的同步細節。

CopyOnWriteArrayList的Java文檔沒有指定它的鎖定特性,但由於它是寫時複製,所以我懷疑它是否在內部進行鎖定。當然,我不會在生產代碼中指望它。

+0

你是說isInactive方法也需要被標記爲synchronized? – jcm

+0

是的,確切地說。這也破壞了地圖的併發性。在這一點上你可能會使用一個普通的舊同步映射。 – markspace

+0

不,讀取操作不需要'synchronized'。 – JimmyB

0

您的toggleId()是線程安全的隔離

我的意思是,如果奇數數量的線程在同一時間撥打toggleId(i)爲同一i,則最終結果將是該i被觸發,如果偶數調用它的同一時間,那麼最終的結果將是無效的。

但是它與isInactive()的關係呢?如果線程A調用isInactive(i),然後線程B調用toggleId(i),然後線程A能夠處理由isInactive(i)返回的值,那麼這意味着什麼?


我想擺脫synchronized塊,但我不知道這是否是可能的。

總之,沒有。只有當ConcurrentHashMap爲你實現了一個線程安全的toggleId類似的函數時,纔有可能,但是我沒有看到它的任何API都適合。 disabledList.putIfAbsent(...)確實一半該工作,但沒有putIfAbsentOrDeleteIfPresent(...)方法。

相關問題