code_money_guji's Blog

Happy coding

CopyOnWriteArrayList

code_money_guji posted @ 2011年2月23日 08:44 in Concurrency in java , 2190 阅读

参考资料:

         JDK 文档

 

CoppyOnWriteArrayList api 说明:

            

ArrayList的一个线程安全的变体,其中所有可变操作(addset 等等)都是通过对底层数组进行一次新的复制来实现的。

这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法 有效。在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。在迭代器上进行的元素更改操作(removesetadd)不受支持。这些方法将抛出 UnsupportedOperationException

允许使用所有元素,包括 null

内存一致性效果:当存在其他并发 collection 时,将对象放入 CopyOnWriteArrayList 之前的线程中的操作Happen-Before随后通过另一线程从 CopyOnWriteArrayList 中访问或移除该元素的操作。

 

解决问题: 但选择使用ArrayList时候, 经常能够在如下示例代码中,在多线程的环境下抛出ConcurrentModificationException:  
示例代码 01:

	private ReentrantLock lock = new ReentrantLock();
	private volatile boolean modified = false;
	private Condition modifiedCondition = lock.newCondition();
	private ArrayList<String> arrayList = null;
	
	public ArrayListCache(ArrayList<String> list){
	     this.arrayList = list;	
	}
	
	public void iterateList(){
		
		lock.lock();
		try{
			
			Iterator<String> iterator = arrayList.iterator();	
			
			while(! modified){
				modifiedCondition.await();
			}
			
			while(iterator.hasNext()){
			      String item = iterator.next();
			      //doSomeThing
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
		
	}
	
	public void addToCache(String item){
		
		lock.lock();
		
		try{
			arrayList.add(item);
			
			modified = true;
			modifiedCondition.signal();
		}finally{
			lock.unlock();
		}
	}

上述代码中,可能会抛出ConcurrentModificationException异常. 这是因为当thread_1在得到当前的arrayList的iterator之后会等待thread_2去调用addToCache方法, 当thread_2更改了list并signal thread_1之后, 由于arrayList被改变, iterator.next()就会抛出异常; 可以给出iterator.next()的源代码如下:

 

                checkForComodification();
	    try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	    } catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	    }

上述的

checkForComodification();

将会抛出此异常;

在这种情况下, 可以考虑使用CopyOnWriteArrayList来代替ArrayList.更改示例代码:

            private ReentrantLock lock = new ReentrantLock();
	private volatile boolean modified = false;
	private Condition modifiedCondition = lock.newCondition();
	private CopyOnWriteArrayList<String> arrayList = null;
	
	public ArrayListCache(ArrayList<String> list){
		if(list != null){
			this.arrayList = new CopyOnWriteArrayList<String>(list);		
		}else{
			//do ypu code if the parameter is null
		}
	     
	}

CopyOnWriteArrayList处理更改操作的时候,会先复制一份,也就是说实在一个copy里面进行的,更改之后将会set到原来的引用上,如下式CopyOnWriteArrayList add方法的源代码参考:

            final ReentrantLock lock = this.lock;
	lock.lock();
	try {
	    Object[] elements = getArray(); //get the orig arrayList
	    int len = elements.length;
	    Object[] newElements = Arrays.copyOf(elements, len + 1);
	    newElements[len] = e;
	    setArray(newElements); //set after update
	    return true;
	} finally {
	    lock.unlock();
	}

to be continue ...


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter