【集合類型的併發】Collections.synchronizedList

 1 :關注要點,爲什麼在有synchroniezed方法的同時會出現 Collections.synchronizedList

 2 :知識背景: 您可能需要了解java Synchronized方法的加鎖的各種機制,包括如何上鎖,鎖對象

 3 : plus: 您需要不斷的深化 Java加鎖的各種機制


Java代碼  

  1. @NotThreadSafe  

  2. class BadListHelper <E> {  

  3.     public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

  4.   

  5.     public synchronized boolean putIfAbsent(E x) {  

  6.         boolean absent = !list.contains(x);  

  7.         if (absent)  

  8.             list.add(x);  

  9.         return absent;  

  10.     }  

  11. }  

 

  這個示例希望實現的功能是爲List提供一個原子操作:若沒有則添加。因爲ArrayList本身不是線程安全的,所以通過集合Collections.synchronizedList將其轉換爲一個線程安全的類,然後通過一個輔助的方法來爲List實現這麼個功能。初看起來這個方法沒問題,因爲也添加了synchronized關鍵字實現加鎖了。

 

但是仔細分析,你會發現問題。首先對於synchronized關鍵字,需要說明的是,它是基於當前的對象來加鎖的,上面的方法也可以這樣寫:

 

Java代碼  

  1. public boolean putIfAbsent(E x) {  

  2.     synchronized(this) {  

  3.         boolean absent = !list.contains(x);  

  4.         if (absent)  

  5.             list.add(x);  

  6.         return absent;  

  7.     }  

  8. }  

 

  所以這裏的鎖其實是BadListHelper對象, 而可以肯定的是Collections.synchronizedList返回的線程安全的List內部使用的鎖絕對不是BadListHelper的對象,應爲你在聲明和初始化這個集合的過程之中,你尚且都不知道這個對象的存在。所以BadListHelper中的putIfAbsent方法和線程安全的List使用的不是同一個鎖,因此上面的這個加了synchronized關鍵字的方法依然不能實現線程安全性。

 

下面給出書中的另一種正確的實現:

  

Java代碼  

  1. @ThreadSafe  

  2. class GoodListHelper <E> {  

  3.     public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

  4.   

  5.     public boolean putIfAbsent(E x) {  

  6.         synchronized (list) {  

  7.             boolean absent = !list.contains(x);  

  8.             if (absent)  

  9.                 list.add(x);  

  10.             return absent;  

  11.         }  

  12.     }  

  13. }  

 

  如果你要分析這個實現是否正確,你需要搞清楚Collections.synchronizedList返回的線程安全的List內部使用的鎖是哪個對象,所以你得看看Collections.synchronizedList這個方法的源碼了。該方法源碼如下:

 

Java代碼  

  1. public static <T> List<T> synchronizedList(List<T> list) {  

  2.     return (list instanceof RandomAccess ?  

  3.                 new SynchronizedRandomAccessList<T>(list) :  

  4.                 new SynchronizedList<T>(list));  

  5.     }  

 

 

通過源碼,我們還需要知道ArrayList是否實現了RandomAccess接口:

 

Java代碼  

  1. public class ArrayList<E> extends AbstractList<E>  

  2.         implements List<E>, RandomAccess, Cloneable, java.io.Serializable  

 

  查看ArrayList的源碼,可以看到它實現了RandomAccess,所以上面的synchronizedList放回的應該是SynchronizedRandomAccessList的實例。接下來看看SynchronizedRandomAccessList這個類的實現:

  

Java代碼  

  1. static class SynchronizedRandomAccessList<E>  

  2.     extends SynchronizedList<E>  

  3.     implements RandomAccess {  

  4.   

  5.         SynchronizedRandomAccessList(List<E> list) {  

  6.             super(list);  

  7.         }  

  8.   

  9.     SynchronizedRandomAccessList(List<E> list, Object mutex) {  

  10.             super(list, mutex);  

  11.         }  

  12.   

  13.     public List<E> subList(int fromIndex, int toIndex) {  

  14.         synchronized(mutex) {  

  15.                 return new SynchronizedRandomAccessList<E>(  

  16.                     list.subList(fromIndex, toIndex), mutex);  

  17.             }  

  18.         }  

  19.   

  20.         static final long serialVersionUID = 1530674583602358482L;  

  21.   

  22.         /** 

  23.          * Allows instances to be deserialized in pre-1.4 JREs (which do 

  24.          * not have SynchronizedRandomAccessList).  SynchronizedList has 

  25.          * a readResolve method that inverts this transformation upon 

  26.          * deserialization. 

  27.          */  

  28.         private Object writeReplace() {  

  29.             return new SynchronizedList<E>(list);  

  30.         }  

  31.     }  

 

因爲SynchronizedRandomAccessList這個類繼承自SynchronizedList,而大部分方法都在SynchronizedList中實現了,所以源碼中只包含了很少的方法,但是通過subList方法,我們可以看到這裏使用的鎖對象爲mutex對象,而mutex是在SynchronizedCollection類中定義的,所以再看看SynchronizedCollection這個類中關於mutex的定義部分源碼:

Java代碼  

  1. static class SynchronizedCollection<E> implements Collection<E>, Serializable {  

  2.     // use serialVersionUID from JDK 1.2.2 for interoperability  

  3.     private static final long serialVersionUID = 3053995032091335093L;  

  4.   

  5.     final Collection<E> c;  // Backing Collection  

  6.     final Object mutex;     // Object on which to synchronize  

  7.   

  8.     SynchronizedCollection(Collection<E> c) {  

  9.             if (c==null)  

  10.                 throw new NullPointerException();  

  11.         this.c = c;  

  12.             mutex = this;  

  13.         }  

  14.     SynchronizedCollection(Collection<E> c, Object mutex) {  

  15.         this.c = c;  

  16.             this.mutex = mutex;  

  17.         }  

  18. }  

  可以看到mutex就是當前的SynchronizedCollection對象,而SynchronizedRandomAccessList繼承自SynchronizedList,SynchronizedList又繼承自SynchronizedCollection,所以SynchronizedRandomAccessList中的mutex也就是SynchronizedRandomAccessList的this對象。所以在GoodListHelper中使用的鎖list對象,和SynchronizedRandomAccessList內部的鎖是一致的,所以它可以實現線程安全性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章