Java并发包中CopyOnWrite容器相关类简介
简介:
本文是主要介绍,并发容器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. 参考内容