CopyOnWriteArrayList理解

CopyOnWrite是一种线程安全的容器, 适用于读多写少的场景, 从字面的理解就可以知道这是一个底层复制的机制,也就是在不影响读的情况下,会采用将原有的数据copy出来,在此基础上进行更新的操作,所以保证了线程的安全,当然,如果写操作非常频繁,不建议读者使用此容器. CopyOnWrite容器多用于并发且读操作频繁的场景.

什么是CopyOnWrite容器

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

下面来看个例子理解下为什么在多并发场景下需要CopyOnWrite容器

public class CopyOnWriteList {
	static class ReadTask implements Runnable {
		private List<String> list = null;

		public ReadTask(List<String> list) {
			this.list = list;
		}

		@Override
		public void run() {
			for (String l : list) {
				System.out.println(l);
			}
		}

	}

	static class WriteTask implements Runnable {
		private List<String> list = null;
		int index = -1;

		public WriteTask(List<String> list, int index) {
			this.list = list;
			this.index = index;
		}

		@Override
		public void run() {
			list.remove(index);
			list.add(index, "add_" + index);
		}
	}
	

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		//List<String> list = new CopyOnWriteArrayList<String>();
		int num = 10;
		for (int i = 0; i < num; i++) {
			list.add(i, "init_" + i);
		}
		ExecutorService executor = Executors.newFixedThreadPool(num);
		for (int i = 0; i < num; i++) {
			executor.execute(new ReadTask(list));
			executor.execute(new WriteTask(list, i));
		}
		executor.shutdown();
	}
}

上面的是一个在并发环境下的读写List的例子,读者运行过这个代码后就会发现,在并发情况下,对容器的读写会抛异常.

add_3Exception in thread "pool-1-thread-3" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
	at java.util.ArrayList$Itr.next(Unknown Source)
	at com.suning.jdk.CopyOnWriteList$ReadTask.run(CopyOnWriteList.java:19)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

感兴趣的读者可以将注释掉的CopyOnWriteArrayList恢复,然后看下运行结果, 在并发情况下,CopyOnWriteArrayList会游刃有余.

下面我们看下源码,究竟CopyOnWriteArrayList内部是怎么做到的

 public boolean add(E paramE)
  {
    ReentrantLock localReentrantLock = this.lock;
    localReentrantLock.lock();
    try
    {
      Object[] arrayOfObject1 = getArray();
      int i = arrayOfObject1.length;
      Object[] arrayOfObject2 = Arrays.copyOf(arrayOfObject1, i + 1);
      arrayOfObject2[i] = paramE;
      setArray(arrayOfObject2);
      boolean bool = true;
      return bool;
    }
    finally
    {
      localReentrantLock.unlock();
    }
  }

转载于:https://my.oschina.net/u/917732/blog/626716

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值