java併發編程之 CopyOnWriteArrayList

我們都知道在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。所以要根據情況來選擇使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章