Java 集合 List CopyOnWriteArrayList

參考連接:java

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

Copy-On-Write簡稱COW,當要修改某個集合時,進行一份集合拷貝,修改新拷貝後的集合,完成後再將新集合指向舊集合。多線程

查看CopyOnWriteArrayList源碼併發

add方法ide

使用final ReentrantLock lock = this.lock;this

lock.lock();.net

在調用add(...)方法前先加鎖,保證同一時間只能有一個線程在添加元素。使用Arrays.copyOf(...)方法複製出另外一個新的數組,並且新的數組的長度比原來多1,拷貝完成後,將新添加的元素賦值給新數組,最後把新的副本數組賦值給舊的數組,而後在finally中釋放鎖。線程

remove方法code

刪除元素,主要判斷是否是刪除最後一個元素,若是是的話,直接複製原來數組的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: http://www.javashuo.com/article/p-fzttzvam-gb.html

2. 數據的一致性:CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。所以對實時性要求的數據,不建議使用COW。

相關文章
相關標籤/搜索