1. 程式人生 > >Java筆試題(三)——執行緒安全、String、StringBuffer、StringBuilder

Java筆試題(三)——執行緒安全、String、StringBuffer、StringBuilder

1.什麼是執行緒安全?

簡單地說法:執行緒安全可以簡單理解為一個方法或者一個例項可以在多執行緒環境中使用而不會出現問題。

複雜的說法:當多個執行緒訪問同一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲取正確的結果,那這個物件是執行緒安全的。

 

2.保證執行緒有什麼方法?

一般說來,確保執行緒安全的方法有這幾個:競爭與原子操作、同步與鎖、可重入、過度優化

競爭與原子操作 
多個執行緒同時訪問和修改一個數據,可能造成很嚴重的後果。出現嚴重後果的原因是很多操作被作業系統編譯為彙編程式碼之後不止一條指令,因此在執行的時候可能執行了一半就被排程系統打斷了而去執行別的程式碼了。一般將單指令的操作稱為原子的(Atomic)

,因為不管怎樣,單條指令的執行不會被打斷的。

因此,為了避免出現多執行緒操作資料的出現異常,Linux 系統提供了一些常用操作的原子指令,確保了執行緒的安全。但是,它們只適用於比較簡單的場合,在複雜的情況下就要選用其他的方法了。

同步與鎖 
為了避免多個執行緒同時讀寫一個數據而產生不可預料的後果,開發人員要將各個執行緒對同一個資料的訪問同步,也就是說,在一個執行緒訪問資料未結束的時候,其他執行緒不得對同一個資料進行訪問。

同步的最常用的方法是使用鎖(Lock),它是一種非強制機制,每個執行緒在訪問資料或資源之前首先試圖獲取鎖,並在訪問結束之後釋放鎖;在鎖已經被佔用的時候試圖獲取鎖時,執行緒會等待,直到鎖重新可用。

二元訊號量是最簡單的一種鎖,它只有兩種狀態:佔用與非佔用,它適合只能被唯一一個執行緒獨佔訪問的資源。對於允許多個執行緒併發訪問的資源,要使用多元訊號量(簡稱訊號量)。

可重入 
一個函式被重入,表示這個函式沒有執行完成,但由於外部因素或內部因素,又一次進入該函式執行。一個函式稱為可重入的,表明該函式被重入之後不會產生任何不良後果。可重入是併發安全的強力保障,一個可重入的函式可以在多執行緒環境下放心使用。

阻止過度優化 
在很多情況下,即使我們合理地使用了鎖,也不一定能夠保證執行緒安全,因此,我們可能阻止對程式碼進行過度的優化以確保執行緒安全。

我們可以使用 volatile 關鍵字試圖阻止過度優化,它可以做兩件事:第一,阻止編譯器為了提高速度將一個變數快取到暫存器而不寫回;第二,阻止編譯器調整操作 volatile 變數的指令順序。

在另一種情況下,CPU 的亂序執行讓多執行緒安全保障的努力變得很困難,通常的解決辦法是呼叫 CPU 提供的一條常被稱作 barrier 的指令,它會阻止 CPU 將該指令之前的指令交換到 barrier 之後,反之亦然。

 

3.String 是不是執行緒安全?為什麼?

String 是執行緒安全的。主要原因是 String 內部儲存字串的 char 陣列以及和 char 陣列相關的資訊都是 final 的,這就保證了String 物件生成的那一刻他在記憶體裡就是不可變的。

 

String:String 類代表字串。

StringBuffer:執行緒安全的可變字元序列。執行緒安全

StringBuilder:一個可變的字元序列。非執行緒安全

三者的區別

1.String 型別的字串物件是不可變的,一旦 String 物件建立後,包含在這個物件中的字元系列是不可以改變的,直到這個物件被銷燬。

2.StringBuilder 和 StringBuffer 型別的字串是可變的。

3.StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是 StringBuffer 中的方法大都採用了 synchronized 關鍵字進行修飾,因此是執行緒安全的。

4.如果是多執行緒環境下涉及到共享變數的插入刪除操作,StringBuffer 則是首選。如果是非多執行緒操作並且有大量的字串拼接,插入,刪除操作則 StringBuilder 是首選。畢竟 String 類是通過建立臨時變數來實現字串拼接的,耗記憶體還效率不高,StringBuilder 是通過 JNI 方式實現終極操作的。

5.StringBuilder 和StringBuffer 的“可變”特性總結如下:
         (1)append,insert,delete 方法最根本上都是呼叫 System.arraycopy() 這個方法來達到目的
         (2)substring(int, int) 方法是通過重新 new String(value, start, end - start) 的方式來達到目的。因此,在執行 substring 操作時,StringBuilder 和 String 基本上沒什麼區別。

6.三者在執行速度方面的比較:StringBuilder > StringBuffer > String。