簡介: 本文是主要介紹,併發容器CopyOnWriteArrayList和CopyOnWriteArraySet(不含重覆元素的併發容器)的基本原理和使用示例。 歡迎探討,如有錯誤敬請指正 如需轉載,請註明出處 http://www.cnblogs.com/nullzx/ 1. CopyOnWrit ...
簡介:
本文是主要介紹,併發容器CopyOnWriteArrayList和CopyOnWriteArraySet(不含重覆元素的併發容器)的基本原理和使用示例。
歡迎探討,如有錯誤敬請指正
如需轉載,請註明出處 http://www.cnblogs.com/nullzx/
1. CopyOnWriteArrayList
從類的名字我們可以看出,該類是基於ArrayList類實現的。而CopyOnWrite的意思顯然借鑒了操作系統中寫時拷貝的思想。該容器主要有以下特點:
1)讀取該容器中元素時,不加鎖。
2)寫操作,會加鎖,也就是說多個線程進行寫入操作時會逐個獲取鎖後進行寫入。
3)寫操作不會影響讀操作,也就是說線程要進行讀操作時,不會因為有線程正在進行寫操作而阻塞。
下麵是寫操作源代碼
public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); E oldValue = get(elements, index); if (oldValue != element) { int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return oldValue; } finally { lock.unlock(); } }
工作原理:在CopyOnWriteArrayList類中,定一了一個數組private transient volatile Object[] array;容器中存儲的對象的索引都會放在這個數組中。
寫操作首先會獲取鎖。當獲取鎖成功後,複製該數組到一個新的數組newElements中,然後修改或者添加某個元素(註意這個時候如果有線程來讀取該數組中的某個值,由於讀操作不需要獲取鎖,所以不會被阻塞,但是可能不能讀取到最新修改後的值)。修改後,讓array指向經過修改後的新數組newElements,原array指向的數組會被垃圾回收器回收。
下麵的代碼是CopyOnWriteArrayList的演示常式
package javalearning; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /*CopyOnWriteArrayList演示常式*/ public class CopyOnWriteArrayListDemo { /*定義一個CopyOnWriteArrayList對象,讀線程和寫線程會同時使用它*/ private CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>(); { /*對CopyOnWriteArrayList對象初始化*/ cowal.add(1); cowal.add(2); cowal.add(3); } private Random rnd = new Random(); public class ReadThread implements Runnable{ private String id; public ReadThread(String id){ this.id = id; } @Override public void run() { try { Thread.sleep(rnd.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } /*讀線程會列印出CopyOnWriteArrayList對象的所有數值*/ System.out.println(id + " " + cowal.toString()); } } public class WriteThread implements Runnable{ /*寫線程會將CopyOnWriteArrayList對象的所有數值加1*/ @Override public void run() { for(int i = 0; i < cowal.size(); i++){ int x = cowal.get(i); /*每修改一個元素之前加鎖*/ cowal.set(i, x+1); /*修改完一個元素後釋放鎖*/ try{ Thread.sleep(rnd.nextInt(1000)); }catch(InterruptedException e){ e.printStackTrace(); } } } } public static void main(String[] args){ ExecutorService es = Executors.newCachedThreadPool(); CopyOnWriteArrayListDemo demo = new CopyOnWriteArrayListDemo(); /*創建兩個讀線程*/ es.execute(demo.new ReadThread("r1")); es.execute(demo.new ReadThread("r2")); /*創建兩個寫線程*/ es.execute(demo.new WriteThread()); es.execute(demo.new WriteThread()); es.shutdown(); while(!es.isTerminated()){ ; } System.out.println("====================="); /*CopyOnWriteArrayList對象中的最終值*/ System.out.println("eventual " + demo.cowal.toString()); } }
全部結束後,最終結果和我們預想的一致。
r2 [2, 3, 3] r1 [2, 4, 3] ===================== eventual [2, 4, 5]
2. CopyOnWriteArraySet
CopyOnWriteArraySet是一個不存貯重覆對象的寫時拷貝容器。它的實現的原理很簡單,在其內部定義了一個CopyOnWriteArrayList對象al,當向該容器添加一個對象時,會調用addIfAbsent方法。
public boolean add(E e) { return al.addIfAbsent(e); }
3. 參考內容
[1] 聊聊併發-Java中的Copy-On-Write容器 | 併發編程網 – ifeve.com
[2] Java併發編程:併發容器之CopyOnWriteArrayList(轉載)