我們都知道在java容器中ArrayList是線程不安全的,而vector是線程安全的。那麼針對線程安全和不安全來說,這兩個容器應該是夠用了,爲什麼還要出現一個CopyOnWriteArrayList這個容器呢?CopyOnWriteArrayList是線程安全的,那麼它較vector有哪些優勢呢?
看一下vector的add和get源碼
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
/**
* Returns the element at the specified position in this Vector.
*
* @param index index of the element to return
* @return object at the specified index
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @since 1.2
*/
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
可以看到對於vector的add和get方法都被synchronize修飾了,這就說明了當我在獲取數據的時候,其他線程也是不能獲取的,所有的讀和寫都會被阻塞。
再來看CopyOnWriteArrayList中的源碼
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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();
}
}
簡單的概括就是,在添加數據的時候,系統會複製一個新的list然後添加的時候往新的array添加,當然這個新的list是lock確保線程安全的。
等新的list操作完成之後,再把舊的list引用的地址指向它即可。但是舊的list是沒有加鎖的,所以讀的操作是不會阻塞的。
copyOnWriterArrayList通過消耗空間的方式來提高系統的併發性,但是這樣浪費了空間,容易造成minor Gc ,嚴重的話會造成full gc。所以要根據情況來選擇使用。