Vector和ArrayList區別以及Vector並非是絕對執行緒安全的
首先看這兩類都實現List介面,而List介面一共有三個實現類,分別是ArrayList、Vector和LinkedList。List用於存放多個元素,能夠維護元素的次序,並且允許元素的重複。3個具體實現類的相關區別如下:
- ArrayList是最常用的List實現類,內部是通過陣列實現的,它允許對元素進行快速隨機訪問。陣列的缺點是每個元素之間不能有間隔,當陣列大小不滿足時需要增加儲存能力,就要講已經有陣列的資料複製到新的儲存空間中。當從ArrayList的中間位置插入或者刪除元素時,需要對陣列進行復制、移動、代價比較高。因此,它適合隨機查詢和遍歷,不適合插入和刪除。
- Vector與ArrayList一樣,也是通過陣列實現的,不同的是它支援執行緒的同步,即某一時刻只有一個執行緒能夠寫Vector,避免多執行緒同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList慢。
- LinkedList是用連結串列結構儲存資料的,很適合資料的動態插入和刪除,隨機訪問和遍歷速度比較慢。另外,他還提供了List介面中沒有定義的方法,專門用於操作表頭和表尾元素,可以當作堆疊、佇列和雙向佇列使用。
- vector是執行緒(Thread)同步(Synchronized)的,所以它也是執行緒安全的,而Arraylist是執行緒非同步(ASynchronized)的,是不安全的。如果不考慮到執行緒的安全因素,一般用Arraylist效率比較高。
如果集合中的元素的數目大於目前集合陣列的長度時,vector增長率為目前陣列長度的100%,而arraylist增長率為目前陣列長度
的50%.如過在集合中使用資料量比較大的資料,用vector有一定的優勢。如果查詢一個指定位置的資料,vector和arraylist使用的時間是相同的,都是0(1),這個時候使用vector和arraylist都可以。而
如果移動一個指定位置的資料花費的時間為0(n-i)n為總長度,這個時候就應該考慮到使用Linkedlist,因為它移動一個指定位置的資料
所花費的時間為0(1),而查詢一個指定位置的資料時花費的時間為0(i)。
ArrayList 和Vector是採用陣列方式儲存資料,此陣列元素數大於實際儲存的資料以便增加和插入元素,
都允許直接序號索引元素,但是插入資料要設計到陣列元素移動 等記憶體操作,所以索引資料快插入資料慢,
Vector由於使用了synchronized方法(執行緒安全)所以效能上比ArrayList要差
,LinkedList使用雙向連結串列實現儲存,按序號索引資料需要進行向前或向後遍歷,但是插入資料時只需要記錄本項的前後項即可,所以插入數度較快!籠統來說: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封裝好的所謂的執行緒安全的類,也要自己好好
思考一下,小心被坑。。。。