前言
前言點擊此處查看:
http://blog.csdn.net/wang7807564/article/details/79113195 同步容器
問題引出:
有N張火車票,每張票都有一個編號,同時有10個視窗對外售票,寫一個類比程式。
public class TicketSeller { static List<String> tickets = new ArrayList<>(); static { for(int i=0; i<10000; i++) tickets.add("票編號:" + i); } public static void main(String[] args) { for(int i=0; i<10; i++) { new Thread(()->{ while(tickets.size() > 0) { System.out.println("銷售了--" + tickets.remove(0)); } }).start(); } }}
在上述程式運行中,會報錯,數組越界:
java.lang.ArrayIndexOutOfBoundsException
很顯然,這是因為多線程之間沒有處理好同步問題造成的。 Vector
嘗試將tickets換為vector類型:
static Vector<String> tickets = new Vector<>();
List介面一共有三個實作類別,分別是ArrayList、Vector和LinkedList.
Vector與ArrayList一樣,也是通過數組實現的,不同的是它支援線程的同步,即某一時刻只有一個線程能夠寫Vector,避免多線程同時寫而引起的不一致性。它的內部是使用synchronized關鍵字來實現同步鎖的。
雖然在筆者測試中沒有沒有出現過數組越界的問題,但是使用Vector是具有潛在的線程不安全風險的。原因是vector對每個讀取和寫入的方法是單獨加鎖的,也就是說使用synchronized關鍵字修飾了讀取和寫入的方法。但是在上面的例子中,讀取和寫入應該作為一個整體,這個整體應該保證原子性,單獨的讀取和寫入雖然加鎖,但是這個整體仍然是線程不安全的,這個整體對應的就是while迴圈的代碼塊。如果想要實現絕對的安全執行緒,可以改成如下例子,但是是犧牲效能的:
new Thread(()-> { synchronized (new Object()) { { while (tickets.size() > 0) { System.out.println("銷售了--" + tickets.remove(0)); } } } }).start();
Collections.synchronizedXXX():
Collections類是一個工廠類,通過這個工廠類來擷取一些同步容器的執行個體。
這些通過Factory 方法擷取到的執行個體,內部也是通過synchronized關鍵字來完成同步的。
Synchronizedxxx方法有兩個不足:
1. 首先,這種方法對於延展性是一種障礙,因為一次只能有一個線程可以訪問hash表。
2. 同時,這樣仍不足以提供真正的執行緒安全性,許多公用的混合操作仍然需要額外的同步。
使用下面的代碼,也能達到解決該問題的目的:
static List<String> tickets = Collections.synchronizedList(new ArrayList<>());
在擷取tickects執行個體的時候,使用collections.synchronizedxxx()函數進行同步,這樣可以擷取一個同步容器的執行個體。避免了數組越界問題。
對於上述問題,如果使用synchronized關鍵字也可以實現:
While(true)Synchronized(tickets){判斷sizeRemove()移除}
這裡面相當於將size()和remove()兩個原子操作合并為一個原子操作,實現了讀寫鎖。但是,這種方法相對來說,更佔用CPU資源。
雖然諸如get()和put()之類的簡單操作可以在不需要額外同步的情況下安全地完成.但還是有一些公用的操作序列,例如迭代操作或者put-if-absent(空則放入),需要外部的同步,以避免資料爭用。
synchronizedMap、synchronizedList等也被稱為有條件的安全執行緒同步的集合封裝器:
也就是說所有單個的操作都是安全執行緒的.但是多個操作組成的操作序列卻可能導致資料爭用,因為在操作序列中控制流程取決於前面操作的結果。
所以,在JDK 5版本以後,對於高並發情境推薦使用ConcurrentHashMap.其特點:效率比Hashtable高,並發性比hashmap好,HashMap中未進行同步考慮,而Hashtable則使用了synchronized,帶來的直接影響就是可選擇,我們可以在單線程時使用HashMap提高效率,而多線程時用Hashtable來保證安全,而ConcurrentHashMap則結合了兩者的特點。