1. 程式人生 > >Vector和ArrayList區別以及Vector並非是絕對執行緒安全的

Vector和ArrayList區別以及Vector並非是絕對執行緒安全的

首先看這兩類都實現List介面,而List介面一共有三個實現類,分別是ArrayList、Vector和LinkedList。List用於存放多個元素,能夠維護元素的次序,並且允許元素的重複。3個具體實現類的相關區別如下:

  1. ArrayList是最常用的List實現類,內部是通過陣列實現的,它允許對元素進行快速隨機訪問。陣列的缺點是每個元素之間不能有間隔,當陣列大小不滿足時需要增加儲存能力,就要講已經有陣列的資料複製到新的儲存空間中。當從ArrayList的中間位置插入或者刪除元素時,需要對陣列進行復制、移動、代價比較高。因此,它適合隨機查詢和遍歷,不適合插入和刪除。
  2. Vector與ArrayList一樣,也是通過陣列實現的,不同的是它支援執行緒的同步,即某一時刻只有一個執行緒能夠寫Vector,避免多執行緒同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList慢。
  3. LinkedList是用連結串列結構儲存資料的,很適合資料的動態插入和刪除,隨機訪問和遍歷速度比較慢。另外,他還提供了List介面中沒有定義的方法,專門用於操作表頭和表尾元素,可以當作堆疊、佇列和雙向佇列使用。
  4. vector是執行緒(Thread)同步(Synchronized)的,所以它也是執行緒安全的,而Arraylist是執行緒非同步(ASynchronized)的,是不安全的。如果不考慮到執行緒的安全因素,一般用Arraylist效率比較高。
  5. 如果集合中的元素的數目大於目前集合陣列的長度時,vector增長率為目前陣列長度的100%,而arraylist增長率為目前陣列長度
    的50%.如過在集合中使用資料量比較大的資料,用vector有一定的優勢。

  6. 如果查詢一個指定位置的資料,vector和arraylist使用的時間是相同的,都是0(1),這個時候使用vector和arraylist都可以。而
    如果移動一個指定位置的資料花費的時間為0(n-i)n為總長度,這個時候就應該考慮到使用Linkedlist,因為它移動一個指定位置的資料
    所花費的時間為0(1),而查詢一個指定位置的資料時花費的時間為0(i)。
    ArrayList 和Vector是採用陣列方式儲存資料,此陣列元素數大於實際儲存的資料以便增加和插入元素,
    都允許直接序號索引元素,但是插入資料要設計到陣列元素移動 等記憶體操作,所以索引資料快插入資料慢,
    Vector由於使用了synchronized方法(執行緒安全)所以效能上比ArrayList要差
    ,LinkedList使用雙向連結串列實現儲存,按序號索引資料需要進行向前或向後遍歷,但是插入資料時只需要記錄本項的前後項即可,所以插入數度較快!

  7. 籠統來說:LinkedList:增刪改快
                  ArrayList:查詢快(有索引的存在)

關於Vector,真是有點坑啊,阿里我嘗試去面試,那位面試官問我,Vector你說是執行緒安全的,那是怎麼實現安全的

我回答:他的方法裡很多都用了synchronized關鍵字啊,實現了同步

他又問我,那Vector還有其他手段實現了同步麼?我當時想,原始碼裡沒別的複雜的東西了,就是靠這個關鍵字搞得

難不成是我沒看到,眼瞎了?有知道的告訴我下啊,(那個modcount不是,就不用跟我說了)

實力坑爹:

先看段程式碼:

    private static Vector<Integer> vector=new Vector();

    public static void main(String[] args) {
        while(true){
            for(int i=0;i<10;i++){
                vector.add(i); //往vector中新增元素
            }
            Thread removeThread=new Thread(new Runnable() {         
                @Override
                public void run() {
                    //獲取vector的大小
                    for(int i=0;i<vector.size();i++){
                        //當前執行緒讓出CPU,使例子中的錯誤更快出現
                        Thread.yield();
                        //移除第i個數據
                        vector.remove(i);
                    }
                }
            });
            Thread printThread=new Thread(new Runnable() {          
                @Override
                public void run() {
                    //獲取vector的大小
                    for(int i=0;i<vector.size();i++){
                        //當前執行緒讓出CPU,使例子中的錯誤更快出現
                        Thread.yield();
                        //獲取第i個數據並列印
                        System.out.println(vector.get(i));
                    }
                }
            });         
            removeThread.start();
            printThread.start();
            //避免同時產生過多執行緒
            while(Thread.activeCount()>20);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

執行之後,會有如下異常:

4
6
6
1
8
3
5
Exception in thread "Thread-285" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 11
    at java.util.Vector.get(Unknown Source)
    at VectorTest$2.run(VectorTest.java:31)
    at java.lang.Thread.run(Unknown Source)
7
9
0
6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這表明上述程式碼在使用Vector的時候執行緒並不是安全的,使用get訪問Vector時出現了越界。這是為什麼呢? 
Vector類中對get以及remove,size方法都加了synchronized關鍵字來保證同步,也就說當一個執行緒呼叫了這些方法時,其他執行緒不能再同時呼叫這些方法。換言之,不能出現兩個及兩個以上的執行緒在同時呼叫這些同步方法。 
那麼為什麼例子中會出現問題呢?這是因為 例子中有些執行緒連續呼叫了兩個或兩個以上的同步方法。 
例子中 removeThread執行緒先呼叫了vector.size()方法獲得vector的大小,接著呼叫vector.remove(i)移除第i個元素;而printThread執行緒也先呼叫vector.size()方法獲得vector的大小,接著呼叫vector.get()獲得第i個元素。

假設此時vector大小為5,此時printThread執行緒執行到 i=4 ,進入for迴圈但在 System.out.println(vector.get(i));之前 printThread執行緒的CPU時間片已到,執行緒printThread轉入就緒狀態; 
此時removeThread執行緒獲得CPU開始執行,把vector的5個元素全刪除了,這是removeThreadCPU時間片已到; 
接著printThread獲得CPU進行執行,由於之前printThread中的i==4,於是呼叫vector.get(4)獲取元素,此時由於vector中的元素已被removeThread執行緒全部刪除,因此報錯。

總的來說,vector保證了其同步方法不能被兩個及兩個以上執行緒同時訪問,但是我們自己的操作會使得即使使用了Vector執行緒也不安全,如果不大清楚,最好自己加上 synchronized進行同步吧。

以上是copy人家的實驗,充分展示了Vector不靠譜的一點,所以即便是應用java封裝好的所謂的執行緒安全的類,也要自己好好

思考一下,小心被坑。。。。