从类的名字我们可以看出,该类是基于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]
CopyOnWriteArraySet是一个不存贮重复对象的写时拷贝容器。它的实现的原理很简单,在其内部定义了一个CopyOnWriteArrayList对象al,当向该容器添加一个对象时,会调用addIfAbsent方法。
- public boolean add(E e) {
- return al.addIfAbsent(e);
- }
3.参考内容
[1] 聊聊并发-Java中的Copy-On-Write容器 | 并发编程网 – ifeve.com
[2] Java并发编程:并发容器之CopyOnWriteArrayList(转载)
来源: http://www.cnblogs.com/nullzx/p/7455919.html