ArrayList和Vector有什么區別?
HashMap和HashTable有什么區別?
StringBuilder和StringBuffer有什么區別?
這些都是Java面試中常見的基礎問題。面對這樣的問題,回答是:ArrayList是非線程安全的,Vector是線程安全的;HashMap是非線程安全的,HashTable是線程安全的;StringBuilder是非線程安全的,StringBuffer是線程安全的。
此時如果繼續問:什么是線程安全?線程安全和非線程安全有什么區別?分別在什么情況下使用?
線程安全:
當多個線程類並發操作某類的某個方法,(在該方法內部)來修改這個類的某個成員變量的值,不會出錯,則我們就說,該的這個方法是線程安全的。
某類的某方法是否線程安全的關鍵是:
(1) 該方法是否修改該類的成員變量;
(2) 是否給該方法加鎖(是否用synchronized關鍵字修飾)。
線程不安全:
當多個線程類並發操作某類的某個方法,(在該方法內部)來修改這個類的某個成員變量的值,很容易就會發生錯誤,故我們就說,這個方法是線程不安全的。如果要把這個方法變成線程安全的,則用 synchronized關鍵字來修飾該方法即可。
注:用 synchronized關鍵字修飾方法,會導致加鎖,雖然可以使該方法線程安全,但是會極大的降低該方法的執行效率,故要慎用該關鍵字。
線程安全:
多個線程(類)同時執行同一段代碼,就可能出現安全問題。
看下面的代碼,來理解線程安全
public Double pi() {
int a = 22;
int b = 7;
return new Double(a / b);
}
現在在執行這個方法時,每一個線程都有自己的獨立的棧區。當線程進入到方法執行斷的時候,一個方法變量在方法代碼段中被創建,並保存在線程的棧區(靜態方法也放在這里)。不同線程執行這段代碼時,會有不同的a/b變量。所以這里是線程安全的,因為沒有數據共享。
考慮下面的例子,多線程情況下只執行一次並可以重用結果:
private Double pi = null;
public Double pi() {
if (pi == null) {
pi = new Double(22 / 7);
}
return pi;
}
這個地方雖然優化了,但可惜他不是線程安全的。
兩個線程並發執行的時候同時進入到pi==null這個位置,這樣可能會new出一個臟的數據.
Consider this example which uses ThreadLocal to make the method pi() again thread-safe while still offering performance gains:
private static ThreadLocal pi = new ThreadLocal();
public Double pi() {
if (pi.get() == null) {
pi.set(new Double(22 / 7));
}
return (Double)pi.get();
}
ThreadLocal類封裝了任何類型對象,並把它綁定到當前線程。線程執行pi()方法的時候,實例pi返回的是當前線程的對象。這樣的調用是線程安全的。
Writing thread-safe code requires you to be careful when using instance variables or static variables, especially when you are modifying objects that may be used by other threads.
用 ArrayList還是 Vector,二者如何取舍?
線程安全:指多線程操作同一個對象的某方法,修改該類的成員變量時,不會出現錯誤。
非線程安全:指多線程操作同一個對象的某方法,修改該類的成員變量時,可能會出現錯誤。
線程安全必須要使用很多synchronized關鍵字來同步控制,所以必然會導致性能的降低。
所以在使用的時候,如果是多個線程操作同一個對象,那么使用線程安全的Vector;否則,就使用效率更高的ArrayList。
非線程安全!=不安全
有人在使用過程中有一個不正確的觀點:我的程序是多線程的,不能使用ArrayList要使用Vector,這樣才安全。
非線程安全並不是多線程環境下就不能使用。注意上面有說到:多線程操作同一個對象。注意是同一個對象
比如最上面那個模擬,就是在主線程中new的一個ArrayList然后多個線程操作同一個ArrayList對象,就會有安全問題。
如果是每個線程中new一個ArrayList,而這個ArrayList只在這一個線程中使用,那么肯定是沒安全問題的。
總結:
若多個線程同時修改某個外部傳來的對象的成員變量,很容易就會出現錯誤,我們稱之為線程不安全。(該類的這個方法是線程不安全的。若要線程安全,用synchronized關鍵字修飾即可)。