Vector是ArrayList的多線程版本,HashTable是HashMap的多線程版本,這些概念我們都很清楚,也被前輩囑咐過很多次,但我們經常會逃避使用Vector和HashTable,因為用得少,不熟嘛!只有在真正需要的時候才會想要使用它們,但問題是什麼時候算真正需要呢?我們來看一個例子,看看使用線程安全的Vector是否可以解決問題,代碼如下:
public static void main(Stringargs){
//火車票列表
final List<String>tickets=new ArrayList<String>();
//初始化票據池
for(int i=0;i<100000;i++){
tickets.add("火車票"+i);
}
//退票
Thread returnThread=new Thread(){
public void run(){
while(true){
tickets.add("車票"+new Random().nextInt());
}
};
};
//售票
Thread saleThread=new Thread(){
public void run(){
for(String ticket:tickets){
tickets.remove(ticket);
}
};
};
//啟動退票線程
returnThread.start();
//啟動售票線程
saleThread.start();
}
模擬火車站售票程序,先初始化一堆火車票,然後開始出售,同時也有退票產生,這段程序有沒有問題?可能會有讀者看出了問題,ArrayList是線程不安全的,兩個線程訪問同一個ArrayList數組肯定會有問題。
沒錯,確定有問題,運行結果如下:
Exception in thread"Thread-1"java.util.ConcurrentModifcationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at Client$2.run(Client.java:28)
運氣好的話,該異常馬上就會報出。也許有人會說這是一個典型錯誤,只須把ArrayList替換成Vector即可解決問題,真的是這樣嗎?我們把ArrayList替換成Vector後,結果照舊,仍然拋出相同的異常,Vector已經是線程安全的,為什麼還報這個錯誤呢?
這是因為他混淆了線程安全和同步修改異常,基本上所有的集合類都有一個叫做快速失敗(Fail-Fast)的校驗機制,當一個集合在被多個線程修改並訪問時,就可能會出現ConcurrentModificationException異常,這是為了確保集合方法一致而設置的保護措施,它的實現原理就是我們經常提到的modCount修改計數器:如果在讀列表時,modCount發生變化(也就是有其他線程修改)則會拋出ConcurrentModificationException異常。這與線程同步是兩碼事,線程同步是為了保護集合中的數據不被髒讀、髒寫而設置的,我們來看線程安全到底用在什麼地方,代碼如下:
public static void main(Stringargs){
//火車票列表
final List<String>tickets=new ArrayList<String>();
//初始化票據池
for(int i=0;i<100000;i++){
tickets.add("火車票"+i);
}
//10個窗口售票
for(int i=0;i<10;i++){
new Thread(){
public void run(){
while(true){
System.out.println(Thread.currentThread().getId()
+"——"+tickets.remove(0));
}
};
}.start();
}
}
還是火車站售票程序,有10個窗口在賣火車票,程序打印出窗口號(也就是線程號)和車票編號,很快我們就會看到這樣的輸出:
13——火車票96531
10——火車票96531
9——火車票96530
16——火車票96530
注意看,上面有兩個線程在賣同一張火車票,這才是線程不同步的問題,此時把ArrayList修改為Vector即可解決問題,因為Vector的每個方法前都加上了synchronized關鍵字,同時只會允許一個線程進入該方法,確保了程序的可靠性。
雖然在系統開發中我們一再說明,除非必要,否則不要使用synchronized,這是從性能的角度考慮的,但是一旦涉及多線程時(注意這裡說的是真正的多線程,不是並發修改的問題,比如一個線程增加,一個線程刪除,這不屬於多線程的範疇),Vector會是最佳選擇,當然自己在程序中加synchronized也是可行的方法。
HashMap的線程安全類HashTable與此相同,不再贅述。
注意 多線程環境下考慮使用Vector或HashTable。