程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> java並發:同步容器&並發容器

java並發:同步容器&並發容器

編輯:JAVA編程入門知識

第一節 同步容器、並發容器

  在Java並發編程中,經常聽到同步容器、並發容器之說,那什麼是同步容器與並發容器呢?同步容器可以簡單地理解為通過synchronized來實現同步的容器,比如Vector、Hashtable以及SynchronizedList等容器,如果有多個線程調用同步容器的方法,它們將會串行執行。

  可以通過查看Vector、Hashtable等同步容器的實現代碼,可以看到這些容器實現線程安全的方式就是將它們的狀態封裝起來,並在需要同步的方法上加上關鍵字synchronized,但在某些情況下,同步容器不一定就是線程安全的,比如獲取最後一個元素或者刪除最後一個元素,我們需要實現額外的同步操作:

public static Object getLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    return list.get(lastIndex);  
}  
      
public static void deleteLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    list.remove(lastIndex);  
} 

  雖然上面的方法看起來沒有問題,Vector自身的方法也是同步的,但是在多線程環境中還是隱藏著問題。如果有兩個線程A,B同時調用上面的兩個方法,假設list的大小為10,這裡計算得到的lastIndex為9,線程B首先執行了刪除操作(多線程之間操作執行的不確定性導致),而後線程A調用了list.get方法,這時就會發生數組越界異常,導致問題的原因就是上面的復合操作不是原子操作,這裡可以通過在方法內部使用list對象鎖來實現原子操作。

  同步容器會導致多個線程中對容器方法調用的串行執行,降低並發性,因為它們都是以容器自身對象為鎖,所以在需要支持並發的環境中,可以考慮使用並發容器來替代。並發容器是針對多個線程並發訪問而設計的,在jdk5.0引入了concurrent包,其中提供了很多並發容器,如ConcurrentHashMap、CopyOnWriteArrayList等。

  其實同步容器與並發容器都為多線程並發訪問提供了合適的線程安全,不過並發容器的可擴展性更高。在Java5之前,程序員們只有同步容器,且在多線程並發訪問的時候會導致爭用,阻礙了系統的擴展性。Java5介紹了並發容器,並發容器使用了與同步容器完全不同的加鎖策略來提供更高的並發性和伸縮性,例如,在ConcurrentHashMap中采用了一種粒度更細的加鎖機制,可以稱為分段鎖,在這種鎖機制下,允許任意數量的讀線程並發地訪問map,並且執行讀操作的線程和寫操作的線程也可以並發的訪問map,同時允許一定數量的寫操作線程並發地修改map,所以它可以在並發環境下實現更高的吞吐量,另外,並發容器提供了一些在使用同步容器時需要自己實現的復合操作,包括putIfAbsent等,但是由於並發容器不能通過加鎖來獨占訪問,所以我們無法通過加鎖來實現其他復合操作了。

 

參考資料:

(1)http://www.cnblogs.com/dolphin0520/p/3933404.html

 

第二節 ConcurrentHashMap

針對並發容器中的ConcurrentHashMap,《java並發編程實戰》一書有如下這樣一段文字:

此處將揭開ConcurrentHashMap的神秘面紗,首先我們看一下ConcurrentHashMap的結構圖,如下:

 

 ConcurrentHashMap的並發度

  ConcurrentHashMap把實際map劃分成若干部分來實現它的可擴展性和線程安全。這種劃分是使用並發度獲得的,它是ConcurrentHashMap類構造函數的一個可選參數,默認值為16,這樣在多線程情況下就能避免爭用。

 

ConcurrentHashMap的鎖分離技術

  HashTable容器在競爭激烈的並發環境下效率低下,原因是所有訪問HashTable的線程都必須競爭同一把鎖。若容器中有多把鎖,每一把鎖用於鎖定容器其中一部分數據,那麼當多線程訪問容器裡不同數據段的數據時,線程間就不會存在鎖競爭,從而可以有效提高並發訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術,首先將數據分成一段一段的存儲,然後給每一段數據配一把鎖,當一個線程占用鎖並訪問其中一個段數據的時候,其他段的數據也能被其他線程訪問。

   對比上圖(該圖摘自網絡),同步容器HashTable實現鎖的方式是鎖整個hash表,而並發容器ConcurrentHashMap的實現方式是鎖桶(簡單理解就是將整個hash表想象成一大缸水,現在將這大缸裡的水分到了幾個水桶裡,hashTable每次都鎖定這個大缸,而ConcurrentHashMap則每次只鎖定其中一個 桶)。

    ConcurrentHashMap將hash表分為16個桶(默認值),諸如get,put,remove等常用操作只鎖當前需要用到的桶。試想,原來只能一個線程進入,現在卻能同時16個線程進入,並發性的提升是顯而易見的。

 

ConcurrentHashMap的remove操作

當對ConcurrentHashMap進行remove操作時,並不是進行簡單的節點刪除操作

  對比上圖,當對ConcurrentHashMap的一個segment(也就是一個桶中的節點)進行remove後,例如,刪除節點C,C節點實際並沒有被銷毀,而是將C節點前面的反轉並拷貝到新的鏈表中,C節點後面的不需要被克隆。這樣的操作使並發的讀線程不受並發的寫線程的干擾,例如,現在有一個讀線程讀到了A節點,寫線程把C刪掉了,但是看上圖,讀線程仍然可以繼續讀下去;當然,如果在刪除C之前讀線程讀到的是D,那麼更不會有影響。

    根據上面所提到的在ConcurrentHashMap中刪除一個節點並不會立刻被讀線程感受到的效果,就是傳說中的弱一致性,所以ConcurrentHashMap的迭代器是弱一致性迭代器

 

參考資料:

本小節僅簡單概述了ConcurrentHashMap的一些內容,其實現機制等可參考以下優質文章

(1)http://www.cnblogs.com/ITtangtang/p/3948786.html

(2)http://ifeve.com/concurrenthashmap/

(3)http://blog.csdn.net/xuefeng0707/article/details/40834595

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved