Java 集合 List CopyOnWriteArrayList

参考链接:

http://blog.csdn.net/hua631150873/article/details/51306021

Copy-On-Write简称COW,当要修改某个集合时,进行一份集合拷贝,修改新拷贝后的集合,完成后再将新集合指向旧集合。

查看CopyOnWriteArrayList源码

add方法

使用final ReentrantLock lock = this.lock;

lock.lock();

在调用add(...)方法前先加锁,保证同一时间只能有一个线程在添加元素。使用Arrays.copyOf(...)方法复制出另一个新的数组,而且新的数组的长度比原来多1,拷贝完成后,将新添加的元素赋值给新数组,最后把新的副本数组赋值给旧的数组,然后在finally中释放锁。

remove方法

删除元素,主要判断是不是删除最后一个元素,如果是的话,直接复制原来数组的length-1,否则先复制数组index前面到新数组,然后再复制index后面的元素到数组中,最后再把新数组赋值给旧数组的引用。

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).  Returns the element that was removed from the list.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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) // 判断index是不是最后一个,举例如果原来数组总共有3个元素,len 是 3, index 是 2, 则numMoved是0;
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1]; // 拷贝一个比原来数组长度少一个的数组
                System.arraycopy(elements, 0, newElements, 0, index); // 先拷贝index前半部分,
                System.arraycopy(elements, index + 1, newElements, index, // 再拷贝index+1后面的部分
                                 numMoved);
                setArray(newElements); // 最后将新数组的引用赋值给旧数组
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

其次看System.arraycopy(...)方法的源码

get方法

set方法

代码示例:

多线程异常也不是一定会出现的,但使用ArrayList有很大机会会出现并发异常,下面改用CopyOnWriteArrayList可以解决这个问题。如果使用ArrayList没有出现并发异常,建议把线程池改大点。

package com.jerry.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CopyOnWriteListTest {
	
	private static final int THREAD_POOL_MAX_NUM = 10;
//	private List<String> mList = new ArrayList<String>();
	private List<String> mList = new CopyOnWriteArrayList<String>();
	
	public static void main(String[] args) {
		new CopyOnWriteListTest().start();

	}
	
	private void initData() {
		this.mList.add("code_99");
		this.mList.add("code_98");
		this.mList.add("code_97");
		
	}
	
	private void start() {
		initData();
		
		ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_MAX_NUM);
		for(int i=1; i<=THREAD_POOL_MAX_NUM; i++) {
			executorService.execute(new ListReader(this.mList));
			String codeId = "code_"+i;
			executorService.execute(new ListWriter(this.mList, codeId));
		}
		executorService.shutdown();
	}
	
	private class ListReader implements Runnable {
		
		private List<String> rList;
		
		public ListReader(List<String> list) {
			this.rList = list;
		}

		@Override
		public void run() {
			if(this.rList != null) {
				System.out.println("Reader Thread: "+Thread.currentThread().getName()+" --> "+this.rList.toString());
			}
			
		}
		
	}
	
	private class ListWriter implements Runnable {
		private List<String> wList;
		private String codeId;
		
		public ListWriter(List<String> list, String codeId) {
			this.wList = list;
			this.codeId = codeId;
		}

		@Override
		public void run() {
			if(this.wList != null) {
				this.wList.add(codeId);
				System.out.println("Writer Thread: "+Thread.currentThread().getName()+" --> "+this.wList.toString());
			}
			
		}
	}

}

CopyOnWriteArrayList的优点和缺点:

优点:解决多线程的并发问题

缺点:

1.内存占用有问题:写时拷贝,内存中会存在两份对象。很明显两个数组同时驻扎在内存中,如果实际应用中,数据比较多,占用内存会比较大,针对这个可以用ConcurrentHashMap来代替。因此,不建议对大对象使用CopyOnWriteArrayList。

ConcurrentHashMap: https://my.oschina.net/u/3781047/blog/1628709

2. 数据的一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。因此对实时性要求的数据,不建议使用COW。

猜你喜欢

转载自my.oschina.net/u/3781047/blog/1627519