Java CopyOnWriteArrayList
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。
从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。
什么是CopyOnWrite容器
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
源码:
add:
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();}}
public void add(int index, E element) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);Object[] newElements;int numMoved = len - index;if (numMoved == 0)newElements = Arrays.copyOf(elements, len + 1);else {newElements = new Object[len + 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index, newElements, index + 1,numMoved);}newElements[index] = element;setArray(newElements);} finally {lock.unlock();}}
View Code
public boolean addAll(int index, Collection<? extends E> c) {Object[] cs = c.toArray();final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);if (cs.length == 0)return false;int numMoved = len - index;Object[] newElements;if (numMoved == 0)newElements = Arrays.copyOf(elements, len + cs.length);else {newElements = new Object[len + cs.length];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index,newElements, index + cs.length,numMoved);}System.arraycopy(cs, 0, newElements, index, cs.length);setArray(newElements);return true;} finally {lock.unlock();}}
View Code
set:
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();}}
remove:
public E remove(int index) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;E oldValue = get(elements, index);int numMoved = len - index - 1;if (numMoved == 0)setArray(Arrays.copyOf(elements, len - 1));else {Object[] newElements = new Object[len - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index,numMoved);setArray(newElements);}return oldValue;} finally {lock.unlock();}}
public boolean remove(Object o) {Object[] snapshot = getArray();int index = indexOf(o, snapshot, 0, snapshot.length);return (index < 0) ? false : remove(o, snapshot, index);}
View Code
private boolean remove(Object o, Object[] snapshot, int index) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] current = getArray();int len = current.length;if (snapshot != current) findIndex: {int prefix = Math.min(index, len);for (int i = 0; i < prefix; i++) {if (current[i] != snapshot[i] && eq(o, current[i])) {index = i;break findIndex;}}if (index >= len)return false;if (current[index] == o)break findIndex;index = indexOf(o, current, index, len);if (index < 0)return false;}Object[] newElements = new Object[len - 1];System.arraycopy(current, 0, newElements, 0, index);System.arraycopy(current, index + 1,newElements, index,len - index - 1);setArray(newElements);return true;} finally {lock.unlock();}}
View Code
void removeRange(int fromIndex, int toIndex) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)throw new IndexOutOfBoundsException();int newlen = len - (toIndex - fromIndex);int numMoved = len - toIndex;if (numMoved == 0)setArray(Arrays.copyOf(elements, newlen));else {Object[] newElements = new Object[newlen];System.arraycopy(elements, 0, newElements, 0, fromIndex);System.arraycopy(elements, toIndex, newElements,fromIndex, numMoved);setArray(newElements);}} finally {lock.unlock();}}
View Code
===============================================================================
多线程对集合进行操作:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Main {public static void main(String[] args) {new CopyOnWriteArrayListDemo().run();} } class CopyOnWriteArrayListDemo {/*** 读线程** @author wangjie*/private static class ReadTask implements Runnable {List<String> list;public ReadTask(List<String> list) {this.list = list;}public void run() {for (String str : list) {System.out.println(str);}}}/*** 写线程** @author wangjie*/private static class WriteTask implements Runnable {List<String> list;int index;public WriteTask(List<String> list, int index) {this.list = list;this.index = index;}public void run() {list.remove(index);list.add(index, "write_" + index);}}public void run() {final int NUM = 5;List<String> list = new ArrayList<String>();//List<String> list=new CopyOnWriteArrayList<String>();for (int i = 0; i < NUM; i++) {list.add("main_" + i);}ExecutorService executorService = Executors.newFixedThreadPool(NUM);for (int i = 0; i < NUM; i++) {executorService.execute(new ReadTask(list));executorService.execute(new WriteTask(list, i));}executorService.shutdown();} }
main_0 Exception in thread "pool-1-thread-2" Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-5" Exception in thread "pool-1-thread-3" java.util.ConcurrentModificationException main_1 write_0 main_1 write_0 write_0 write_0 write_1 write_2at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) write_3at java.util.ArrayList$Itr.next(ArrayList.java:851) main_4at com.qhong.CopyOnWriteArrayListDemo$ReadTask.run(Main.java:28)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745) java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at com.qhong.CopyOnWriteArrayListDemo$ReadTask.run(Main.java:28)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745) java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at com.qhong.CopyOnWriteArrayListDemo$ReadTask.run(Main.java:28)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745) java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at com.qhong.CopyOnWriteArrayListDemo$ReadTask.run(Main.java:28)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)
把注释的取消,换成CopyOnWriteArrayList
main_0 main_1 main_2 main_3 main_4 main_0 write_1 write_2 main_3 main_4 write_0 write_1 write_2 write_3 write_0 write_1 write_2 main_4 write_4 write_0 write_1 write_2 write_3 write_4
View Code
=============================================================================================
Array一边遍历,一边删除
foreach:
import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};//System.out.println(Arrays.toString(strArray));List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);for(String str:list){System.out.println(str);list.remove(str);System.out.println("移除后:"+list);}} }
[111, 222, 333, 444] Exception in thread "main" java.util.ConcurrentModificationException 111 移除后:[222, 333, 444]at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)at java.util.ArrayList$Itr.next(ArrayList.java:851)at com.qhong.Main.main(Main.java:15)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
发现报错
for:
import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};//System.out.println(Arrays.toString(strArray));List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);for(int i=0;i<list.size();i++){System.out.println(list.get(i));list.remove(i);//list.remove(list.get(i)); System.out.println(list);}} }
[111, 222, 333, 444] 111 [222, 333, 444] 333 [222, 444]
不报错,但是跟我们想要的不一样,删一个,漏一个
迭代器:
import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};//System.out.println(Arrays.toString(strArray));List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);Iterator<String> it = list.iterator();while(it.hasNext()){it.next();it.remove();System.out.println(list);}} }
[111, 222, 333, 444] [222, 333, 444] [333, 444] [444] []
可以使用。
CopyOnWriteArrayList:
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);for(String item:list2){list2.remove(item);System.out.println(list2);}} }
[111, 222, 333, 444] [111, 222, 333, 444] [222, 333, 444] [333, 444] [444] []
可以正常使用
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);for(int i=0;i<list2.size();i++){String item=list2.get(i);list2.remove(item);System.out.println(list2);}} }
[111, 222, 333, 444] [111, 222, 333, 444] [222, 333, 444] [222, 444]
跟ArrayList结果一样。
理想的应该这样:
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);for(int i=0;i<list2.size();){String item=list2.get(i);list2.remove(item);System.out.println(list2);}} }
View Code
总的对于一边遍历一边remove来说,foreach这种的有问题。
==================================================================
CopyOnWriteArrayList一边遍历,一遍添加
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);for(int i=0;i<list2.size();i++){if(i<6) {String item = list2.get(i);list2.add("sss");System.out.println(list2);}}} }
[111, 222, 333, 444] [111, 222, 333, 444] [111, 222, 333, 444, sss] [111, 222, 333, 444, sss, sss] [111, 222, 333, 444, sss, sss, sss] [111, 222, 333, 444, sss, sss, sss, sss] [111, 222, 333, 444, sss, sss, sss, sss, sss] [111, 222, 333, 444, sss, sss, sss, sss, sss, sss]
foreach
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);for(String item:list2) {list2.add("sss");System.out.println(list2);}} }
[111, 222, 333, 444] [111, 222, 333, 444] [111, 222, 333, 444, sss] [111, 222, 333, 444, sss, sss] [111, 222, 333, 444, sss, sss, sss] [111, 222, 333, 444, sss, sss, sss, sss]
发现没有对foreach遍历产生影响。
迭代器:
import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;public class Main {public static void main(String[] args) {String[] strArray={"111","222","333","444"};List<String> list=new ArrayList<String>();list.addAll(Arrays.asList(strArray));System.out.println(list);CopyOnWriteArrayList<String> list2=new CopyOnWriteArrayList<String>(list);System.out.println(list2);Iterator<String> it = list2.iterator();while(it.hasNext()){it.next();list2.add("sss");System.out.println(list2);}} }
[111, 222, 333, 444] [111, 222, 333, 444] [111, 222, 333, 444, sss] [111, 222, 333, 444, sss, sss] [111, 222, 333, 444, sss, sss, sss] [111, 222, 333, 444, sss, sss, sss, sss]
CopyOnWrite的缺点
CopyOnWrite容器有很多优点,但是同时也存在两个问题,即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。
内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象 。
如果这些对象占用的内存比较大,比如说200M左右,那么再写入100M数据进去,内存就会占用300M,那么这个时候很有可能造成频繁的Yong GC和Full GC。
针对内存占用问题,可以通过压缩容器中的元素的方法来减少大对象的内存消耗,比如ConcurrentHashMap.
数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
http://www.cnblogs.com/dolphin0520/p/3938914.html
http://www.cnblogs.com/alipayhutu/archive/2012/08/11/2634073.html
https://my.oschina.net/jielucky/blog/167198
http://ifeve.com/java-copy-on-write/
Java CopyOnWriteArrayList相关推荐
- Java中的CopyOnWriteArrayList
介绍: Java中的CopyOnWriteArrayList是List接口的线程安全实现. 它属于java.util.concurrent包,是ArrayList实现的增强版本. 顾名思义, Copy ...
- HowToDoInJava Java 教程·翻译完成
原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 核心 Java 教程 什 ...
- Exception in thread “Thread-2“ java.util.ConcurrentModificationException异常的解决方案
ConcurrentModificationException异常的解决方案 ConcurrentModificationException异常 ConcurrentModificationExcep ...
- java线程之List集合并发安全问题及解决方案
一.问题代码 任务:执行10轮次,使用多线程,给list集合添加元素,查看每次执行的结果. public static void main(String[] args) throws Interrup ...
- java foreach delete_Java CopyOnWriteArrayList forEach()用法及代码示例
CopyOnWriteArrayList的forEach()方法为Iterable的每个元素执行给定的操作,直到已处理完所有元素或该操作引发异常. 用法: public void forEach (C ...
- Java基础之CopyOnWriteArrayList
前言 今天我们来学习一下CopyOnWriteArrayList这个比ArrayList更安全的集合,同时带着以下几个问题去分析源码. CopyOnWriteArrayList如何保证线程安全? Co ...
- Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)
文章目录 Pre 概述 fail-safe的容器-CopyOnWriteArrayList add remove函数 例子 缺陷 使用场景 Pre Java - Java集合中的快速失败Fail Fa ...
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- copyof java_死磕 java集合之CopyOnWriteArrayList源码分析
简介 CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不 ...
最新文章
- CSS大小设置实例——盒子模型
- msfvenom java_Msfvenom命令总结大全
- 初步学习“C#枚举”
- saber软件安装后怎么打开_教程:新《Beat Saber》mod安装工具已推出
- Aix5.3安装Bash Shell环境
- 蛇形填数 ------- 模拟水题
- HDOJ--2112--
- PHP 将微信录音arm格式文件转mp3格式
- mysql车库管理系统_小区停车管理系统(JSP+JAVA+MySQL)
- 使用QT写的串口调试助手源代码分享(一)
- 网络电台mms地址大全(一)
- 一文看懂DSP的DMA传输(burst、transfer、wrap)
- CSS W3C 统一验证工具
- CollectionUtils取交集,并集和差集
- 飞塔60d带宽_飞塔防火墙划分带宽 万兆防火墙
- 蕾哈娜首个护肤品牌全球发售;凯悦首旅如家合资首批五家逸扉酒店揭幕 | 美通企业日报...
- imperva-syslog日志配置方法
- trace系列0 - 概述
- Qt中使用QByteArray读文件得到的数据后转成int
- FusionCharts_API_中文帮助文档!
热门文章
- Oracle推出轻量级Java微服务框架Helidon
- 如何真正理解用Nginx代理来解决同源策略
- [译] How to NOT React:React 中常见的反模式与陷阱
- openstack安装系列问题:window7 64位安装的virtualBox 只能选择32位的系统安装不能选择64位的系统安装...
- The encryption certificate of the relying party trust identified by thumbprint is not valid
- 收购美新!中国MEMS传感器和国外差距多大
- centos cp 详解
- 20170307-1
- Spring 源码分析(四) ——MVC(一)Web 基础
- C语言读写伯克利DB 4