`
kingj
  • 浏览: 425647 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

java CopyOnWriteArrayList的使用

 
阅读更多

除了加锁外,其实还有一种方式可以防止并发修改异常,这就是将读写分离技术(不是数据库上的)。

先回顾一下一个常识:

1、JAVA中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,几乎不需要CPU时间。

2、JAVA中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。

 

基于上面这个常识,我们再来探讨下面这个问题:

在CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过JDK1.6的Arrays.copyof()来生成一份新的数组

然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象(这里应用了常识1),这样保证了每次写都是在新的对象上(因为要保证写的一致性,这里要对各种写操作要加一把锁,JDK1.6在这里用了重入锁),

然后读的时候就是在引用的当前对象上进行读(包括get,iterator等),不存在加锁和阻塞,针对iterator使用了一个叫 COWIterator的阉割版迭代器,因为不支持写操作,当获取CopyOnWriteArrayList的迭代器时,是将迭代器里的数据引用指向当前 引用指向的数据对象,无论未来发生什么写操作,都不会再更改迭代器里的数据对象引用,所以迭代器也很安全(这里应用了常识2)。

CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之 间也不需要加锁,读和写之间的同步处理只是在写完后通过一个简单的“=”将引用指向新的数组对象上来,这个几乎不需要时间,这样读操作就很快很安全,适合 在多线程里使用,绝对不会发生ConcurrentModificationException ,所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

 

 

 

 

 

   在你的应用中有一个列表(List),它被频繁的遍历,但是很少被修改。像“你的主页上的前十个分类,它被频繁的访问,但是每个小时通过Quartz的Job来调度更新”。

如果你使用ArrayList来作为该列表的数据结构并且不使用同步(synchronization),你可能会遇到ConcurrentModificationException,因为在你使用Quartz的Job修改该列表时,其他的代码可能正在遍历该列表。

 

    有些开发人员可能使用Vector或Collections.synchronizedList(List<T>)的方式来解决该问题。但是这并没有效果!虽然在列表上add(),remove()和get()方法现在对线程是安全的,但遍历时仍然会抛出ConcurrentModificationException!在你遍历在列表时,你需要在该列表上使用同步,同时,在使用Quartz修改它时,也需要使用同步机制。这对性能和可扩展性来说是一个噩梦。同步需要在所有的地方出现,仅仅是因为每个小时都需要做更新。

 

     幸运的是,这里有更好的解决方案。使用CopyOnWriteArrayList。

当列表上的一个结构修改发生时,一个新的拷贝(copy)就会被创建。这在经常发生修改的地方使用,将会很低效。遍历该列表将不会出现ConcurrentModificationException,因为该列表在遍历时将不会被做任何的修改。

另一种避免添加同步代码但可以避免并发修改问题的方式是在调度任务中构建一个新的列表,然后将原来指向到列表上的引用赋值给新的列表。在JVM中,赋值一个新的引用是原子操作。这种方式在使用旧的遍历方式(for (int i=0; i<list.size(); i++) { … list.get(i) …})时将无效(也会出错)。切换的列表中的大小将引发新的错误产生。更加糟糕的是因为改变是在不同的线程中发生的,所以还会有很多潜在的问题。使用volatile关键字可能会有所帮助,但是对列表大小的改变依然会有问题。

 

     内存一致性和刚发生后保证了CopyOnWriteArrayList的可用性。同时,代码变得更简单,因为根本不需要使用volatile关键字或同步。更少的代码,更少的bug!

 

     CopyOnWriteArrayList的另一个使用案例是观察者设计模式。如果事件监听器由多个不同的线程添加和移除,那么使用CopyOnWriteArrayList将会使得正确性和简单性得以保证。

分享到:
评论
2 楼 cesymm 2014-02-12  
用copyOnWriteList在数据量较大的时候,性能会下降很厉害。

每次的add都会数据拷贝而ArrayList不会,性能较高


我一般的解决方案,还是引用切换的原子操作

另一种避免添加同步代码但可以避免并发修改问题的方式是在调度任务中构建一个新的列表,然后将原来指向到列表上的引用赋值给新的列表。在JVM中,赋值一个新的引用是原子操作。这种方式在使用旧的遍历方式(for (int i=0; i<list.size(); i++) { … list.get(i) …})时将无效(也会出错)。切换的列表中的大小将引发新的错误产生。更加糟糕的是因为改变是在不同的线程中发生的,所以还会有很多潜在的问题。使用volatile关键字可能会有所帮助,但是对列表大小的改变依然会有问题。



List old = new ArrayList();

List temp = old;

for(int i=0;i<temp.size;i++){
    ...temp.get(i)
}
或iterator的遍历


1 楼 lg_asus 2013-10-02  
这个类不能用下标去遍历,而要用iterator.
如果用下标遍历的话,与此同时另一个线程去修改了COW,那么可能会报下标out of array.
code:

public class COWT {

	public static void main(String...args) throws InterruptedException
	{
		final CopyOnWriteArrayList<Integer> cowList = new CopyOnWriteArrayList<Integer>();
		for(int i=0;i<10;i++)
		{
			cowList.add(i);
		}
		
		new Thread(){
			@Override
			public void run() {
				for(int i=0;i<cowList.size();i++)
				{
					try {
						Thread.currentThread().sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(cowList.get(i));
				}
//				Iterator<Integer> it = cowList.iterator();
//				while(it.hasNext())
//				{
//					try {
//						Thread.currentThread().sleep(1);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//					System.out.println(it.next());
//				}
			};
		}.start();
		
		new Thread(){
			@Override
			public void run() {
				try {
					Thread.currentThread().sleep(3);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				cowList.clear();
			};
		}.start();
		
	}
}


相关推荐

    java并发容器CopyOnWriteArrayList实现原理及源码分析

    另外,CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,不会抛出ConcurrentModificationException异常。 CopyOnWriteArrayList容器的缺点是...

    java集合-CopyOnWriteArrayList的使用

    在Java中,CopyOnWriteArrayList(写入时复制数组列表)是线程安全的集合类,它实现了List接口,并使用了"写入时复制"的策略来保证线程安全性。 CopyOnWriteArrayList的主要特点是:在进行修改操作(例如添加、修改...

    Java中CopyOnWriteArrayList的使用

    java中,List在遍历的时候,如果被修改了会抛出java.util.ConcurrentModificationException错误。  看如下代码: import java.util.ArrayList; import java.util.List; public class Resource3 { public ...

    Java的CopyOnWriteArrayList功能详解,及无法保证数据是实时同步.docx

    Java中的`CopyOnWriteArrayList`是一个线程安全的列表实现,特别适合于高并发环境下的读多写少的场景。这个类的名字暗示了其工作原理:在修改(写入)时复制原有的数组,然后在新的数组上进行操作,最后将新数组替换...

    深入解析Java中的CopyOnWriteArrayList:工作原理与应用

    在Java的并发编程中,CopyOnWriteArrayList 是一个重要的线程安全集合类,它通过写时复制(Copy-On-Write)机制实现了高效的读操作。本文将详细探讨 CopyOnWriteArrayList 的工作原理、优缺点、适用场景以及代码示例...

    java遍历时可修改的容器CopyOnWriteArrayList.html

    java遍历时可修改的容器CopyOnWriteArrayList

    Java concurrency集合之 CopyOnWriteArrayList_动力节点Java学院整理

    Java concurrency集合之 CopyOnWriteArrayList_动力节点Java学院整理,动力节点口口相传的Java黄埔军校

    Java源码解析CopyOnWriteArrayList的讲解

    Java源码解析CopyOnWriteArrayList的讲解 Java源码解析CopyOnWriteArrayList是Java集合框架中一个非常重要的组件,它提供了一个线程安全的ArrayList变种,用于处理大量读取操作和少量写入操作的场景。在本文中,...

    (PDF带目录)《Java 并发编程实战》,java并发实战,并发

    3. **并发容器**:书中详细讨论了`java.util.concurrent`包下的并发容器,如`ConcurrentHashMap`、`CopyOnWriteArrayList`和`BlockingQueue`等。这些容器设计为线程安全,可以提高多线程环境下的性能。 4. **并发...

    java vector 使用实例

    因此,在多线程环境下,建议使用`Collections.synchronizedList()`或`CopyOnWriteArrayList`来创建线程安全的列表。 `vectorTest`可能是包含`Vector`使用实例的源代码文件,它可能演示了上述操作的实际应用。通过...

    java线程使用教程

    ### Java线程使用教程知识点详解 #### 一、线程基础概述 - **定义与特点**:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。Java是首个在语言级别明确支持线程特性的...

    第三章 CopyOnWriteArrayList源码解析1

    由于CopyOnWriteArrayList使用了final的ReentrantLock,所以确保了锁的不可变性,增强了并发安全性。 2. 获取锁,确保同一时刻只有一个线程能执行修改操作。 3. 检查是否需要扩容。如果当前数组已满,或者在某些...

    Java 多线程与并发(14-26)-JUC集合- CopyOnWriteArrayList详解.pdf

    ### Java多线程与并发(14-26)-JUC集合-CopyOnWriteArrayList详解 #### 一、概述 `CopyOnWriteArrayList`是一种线程安全的集合类,它是`ArrayList`的一种特殊版本,主要通过复制底层数组的方式来保证线程安全性。...

    【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    Java 容器使用注意事项总结 源码分析: ArrayList 核心源码+扩容机制分析 LinkedList 核心源码分析 HashMap 核心源码+底层数据结构分析 ConcurrentHashMap 核心源码+底层数据结构分析 LinkedHashMap 核心源码分析 ...

    Java最新开发手册(黄山版)

    3. 并发集合:探讨Java并发编程中的ConcurrentHashMap、CopyOnWriteArrayList等线程安全的集合类。 三、泛型 1. 泛型的概念:解释泛型的基本用法,如何限制类型参数,以及通配符的使用。 2. 泛型方法与泛型类:探讨...

    Java基础尚硅谷宋红康学习笔记

    10. **Java并发编程**:包括线程池、锁机制(如synchronized、ReentrantLock)、并发容器(如ConcurrentHashMap、CopyOnWriteArrayList)以及并发工具类(如CountDownLatch、CyclicBarrier)。 这些是Java基础知识...

    Java并发编程实践(java concurrency in practice)pdf (java多线程总结.ppt)

    6. **并发集合**:Java提供了线程安全的集合,如`ConcurrentHashMap`、`CopyOnWriteArrayList`等,这些集合在并发环境中性能优异。书中会分析它们的设计原理和使用场景。 7. **线程池**:`ExecutorService`是线程池...

    javajava相关文档

    文档可能还会深入到Java的并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等,它们是线程安全的,适合多线程环境下的数据存储和操作。线程安全的类通常使用了内部锁机制(如synchronized)或者其他并发控制...

Global site tag (gtag.js) - Google Analytics