【Java集合】除了Vector,還有另一個提供線程安全的List是什麼?

前言

金三銀四,勢在必得。我自信又有點緊張地走進了面試現場。

面試官:“我們先聊點Java基礎吧,除了Vector,還有另一個提供線程安全的List是什麼?”。

我:“我知道可以通過Collections.synchronizedList()方法,將線程不安全的List轉爲線程安全。"

面試官笑着說:“那麼Collections.synchronizedList()的實現原理是什麼?“。

我:“額…這個我不是很清楚”。

無疑,這這觸及到了我的知識盲點。

面試官:“你可以瞭解一下SynchronizedList,沒事,我們到下一個問題哈…"

面試結束後,我留下了學海無涯的淚水…

對於這個問題,我們的回答思路可以是這樣:

  • SynchronizedList是什麼呢?
  • 分析Vector與SynchronizedList之間的區別是什麼?
  • 總結Vector和SynchronizedList之間的區別。

(若文章有不正之處,或難以理解的地方,請多多諒解,歡迎指正)

SynchronizedList是什麼?

在瞭解SynchronizedList之前,讓我們先看看SynchronizedList在什麼位置:
在這裏插入圖片描述
我們可以看到,SynchronizedList結合了SynchronizedCollection和List的特性,這是有的小夥伴會在編輯器召喚這個類,但發現,怎麼召喚不出來?因爲,SynchronizedList是Collections的靜態內部類

可以看到,將線程不安全的List轉爲線程安全的Collections.synchronizedList()方法,是通過生成SynchronizedList對象來實現的:

static <T> List<T> synchronizedList(List<T> list, Object mutex) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list, mutex) :
                new SynchronizedList<>(list, mutex));
    }

分析Vector與SynchronizedList之間的區別是什麼?

上文我們知道,SynchronizedList是java.util.Collections的靜態內部類,那麼Vector呢?
在這裏插入圖片描述
可以看到,Vector跟SynchronizedList都是List的小弟,而且Vector是java.util包中的一個類。

比較二者幾個重要的方法

1. add方法

Vector的實現:

public void add(int index, E element) {
    insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
    modCount++;
    if (index > elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
    }
    ensureCapacityHelper(elementCount + 1);
    System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    elementData[index] = obj;
    elementCount++;
}
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

可以看到Vector的insertElementAt()方法使用了synchronized關鍵字來實現線程安全。不過這段代碼的操作好面熟啊。是的,其實Vector與ArrayList除了線程安全方面有所不同,兩者之間的代碼都很相似,有興趣的朋友可以看這篇文章:【Java集合】ArrayList的使用及原理

我們來看看ArrayList的add()方法代碼,除了沒有synchronized修飾方法,還真的很相似:

public void add(int index, E element) {
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

ps:在面試的時候可以稍稍帶過ArrayList的知識點,如ArrayList與Vector之間的不同有以下方面:

  • 線程安全:ArrayList是線程不安全的,Vector是線程安全的。
  • 擴容機制:ArrayList擴容爲原始容量的1.5倍,而Vector擴容爲原始容量的2倍。

SynchronizedList的實現:

public void add(int index, E element) {
   synchronized (mutex) {
       list.add(index, element);
   }
}

在SynchronizedList在初始化的時候,會傳入一個List:

SynchronizedList(List<E> list) {
    super(list);
    this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
    super(list, mutex);
    this.list = list;
}

而這裏的list.add()會根據傳入的List類型,決定SynchronizedList.add()的實現方式

從上面代碼可以看到,Vector使用同步方法實現線程安全,而SynchronizedList使用代碼塊實現

2. remove方法

SynchronizedList的實現:

public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
}

SynchronizedList中的remove()方法的實現,還是那麼地隨和,雖然加上了synchronized代碼塊,但remove()方法實現是那麼地親切,完全入鄉隨俗地按照傳入的List類的remove()方法來進行元素的移除

Vector的實現:

public synchronized E remove(int index) {
        modCount++;
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        E oldValue = elementData(index);

        int numMoved = elementCount - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--elementCount] = null; // Let gc do its work

        return oldValue;
    }

ArrayList的實現:

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

通過以上兩段代碼的比對,可以看到除了Vector的remove()方法使用了synchronized關鍵字來實現線程安全之外,二者的實現十分相似。

總結Vector與SynchronizedList之間的不同

在總結之前,我們先回顧一下,Vector線程同步是通過同步方法來實現,SynchronizedList線程同步是通過同步代碼塊來實現,我們先區分一下這兩種同步方式。

  1. 同步代碼塊在鎖定的範圍上可能比同步方法要小,但一般鎖的範圍大小和性能是成反比的。
  2. 同步塊可以更加精準地控制鎖的作用域,而同步方法的鎖的作用域是整個方法。
  3. 同步代碼塊可以選擇對哪個對象加鎖,但是同步方法只能給this對象加鎖。

目前爲止,我們可以看到Vector與SynchronizedList之間的不同在於:

  1. 擴容機制:Vector的擴容機制是固定的,而SynchronizedList可以根據出傳入List的不同,適配不同List的擴容規則。
  2. :SynchronizedList可以指定鎖定的對象。

但其實還有兩點我們忽略了:

  • SynchronizedList中的listIterator和listIterator(int index)並沒有做同步處理,而Vector卻對它們上了鎖,也就是說在使用SynchronizedList進行遍歷的時候要手動加鎖。
  • 將繼承了List類的子類轉換成SynchronizedList,是不需要改變它的底層數據結構,可以直接使用它本身的方法。而Vector底層結構就是使用數組實現的,所以Vector不能像SynchronizedList那樣適配其他List類。

所以SynchronizedList和Vector之間最主要的區別在於:

  1. SynchronizedList有很好的兼容能力,無需改變List類的子類的數據結構,就可以將它們轉換成線程安全的類,而Vector不具備這種能力。
  2. SynchronizedList進行遍歷時要手動進行同步處理,但Vector的遍歷方法是線程安全的。
  3. SynchronizedList可以指定鎖定的對象,但是Vector的鎖定範圍是方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章