2014年9月25日 星期四

[Java] Thread-safe 問題

之前在面試的時候被問過:如何解決Thread-saafe的問題。
那個時候回答不出來,之前學習時有講到執行緒 ( Thread ) ,但是沒有進一步討論Thread-safe的問題。現在這間公司由於是撰寫JSP,JSP經過APServer編譯後,也是使用Thread來執行,前輩提到這個問題時才想起之前有面試的一家公司問過這個問題。

要解決這個問題很簡單,以下參考Gossip@Openhome

容器類預設沒有考慮執行緒安全問題,您必須自行實作同步以確保共用資料在多執行緒存取下不會出錯,例如若您使用 List物件時,您可以這樣實作:
// arraylist參考至一個ArrayList的一個實例 
synchronized(arraylist) {
    arraylist.add(new SomeClass()); 
}
事實上,您也可以使用Collections的synchronizedXXX()等方法來傳回一個同步化的容器物件,例如傳回一個同步化的List:
List list = Collections.synchronizedList(new ArrayList());
以這種方式返回的List物件,在存取資料時,會進行同步化的工作,不過在您使用Iterator遍訪物件時,您仍必須實作同步化,因為這樣的List使用iterator()方法返回的Iterator物件,並沒有保證執行緒安全(Thread-safe),一個實作遍訪的例子如下:
List list = Collections.synchronizedList(new ArrayList());
      ...
synchronized(list) {
      Iterator i = list.iterator(); 
      while (i.hasNext())
          foo(i.next());
}
在J2SE 5.0之後,新增了 java.util.concurrent 這個 package,當中包括了一些確保執行緒安全的 Collection 類,例如 ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet等等,這些新增的 Collection 類基本行為與先前介紹的Map、List、Set等物件是相同的,所不同的是增加了同步化的功能,而且依物件的特性不同而有不同的同步化實作,以確保效率與安全性。

例如ConcurrentHashMap,它針對Hash Table中不同的區段(segment)進行同步化,而不是對整個物件進行同步化,預設上HashMap有16個區段,當有執行緒在存取第一個區段時, 第一個區域進入同步化,然而另一個執行緒仍可以存取第一個區段以外的區段,而不用等待第一個執行緒存取完成,所以與同步化整個物件來說,新增的這些同步化 物件,在效率與安全性上取得了較好的平衡。

沒有留言:

張貼留言