我正試圖理解StringBuilder和StringBuffer之間的區別。下面程式的目標是,2個執行緒(Jack和Jill)競爭修改一個StringBuffer和StringBuilder的值。如果原始值已經被修改,那么執行緒將不會修改該變數。 為什么我的StringBuffer變數不是同步的。而StringBuilder變數卻表現為同步的
。我的預期輸出是...
。StringBuffer。杰克贏了 StringBuilder。Jack wonJill won
而實際的輸出是相反的。
public class StringBufferIsThreadSafe{
public static void main title function_">main(String[] args) throws InterruptedException {
Hello hello = new Hello()。
Thread jackThread = new Thread(new Jack(Hello. strBuf, hello.strBuilder))。)
Thread jillThread = new Thread(new Jill(hello. strBuf, hello.strBuilder))。)
jackThread.start()。
jillThread.start()。
jackThread.join()。
jillThread.join()。
System.out.println("StringBuffer: "/span> hello.strBuf)。
System.out.println("StringBuilder: "/span> hello.strBuilder)。
}
}
class Jack implements Runnable{
private StringBuffer strBuf;
private StringBuilder strBuilder;
public Jack(StringBuffer strBuf, StringBuilder strBuilder){
this.strBuf = strBuf;
this.strBuilder = strBuilder。
}
@Override
public void run() {
try {
Thread.sleep(3000)。
if(this.strBuf.toString().eals("") {
this.strBuf.append("Jack won") 。
}
if(this.strBuilder.toString().eals("") {
this.strBuilder.append("Jack won") 。
}
} catch (InterruptedException e) {
e.printStackTrace()。
}
}
class Jill implementsRunnable{
private StringBuffer strBuff;
private StringBuilder strBuilder;
public Jill(StringBuffer strBuff, StringBuilder strBuilder) {
this.strBuff = strBuff;
this.strBuilder = strBuilder。
}
@Override
public void run() {
try {
Thread.sleep(3000)。
if(this.strBuff.toString().equals(""/span>)) {
this.strBuff.append("Jill won") 。
}
if(this.strBuilder.toString().equals("" )) {
this.strBuilder.append("Jill won") 。
}
} catch (InterruptedException e) {
e.printStackTrace()。
}
}
class Hello {
StringBuffer strBuf = new StringBuffer() 。
StringBuilder strBuilder = new StringBuilder() 。
public Hello() {
this.strBuf.append("") 。
this.strBuilder.append("") 。
}
uj5u.com熱心網友回復:
我認為你誤解了'內部同步'的含義。
'內部同步'并不是巫術魔法。StringBuffder中的代碼根本不可能影響你的代碼。當你寫道:
這將成為一個問題。
if (strBuffer.toString().equals(""/span>)) {
strBuffer.append("Jill won")。
}
你在這里沒有得到任何保證,特別是你似乎認為這整個操作現在在某種程度上是原子的(因為在這之后,strBuffer不可能包含,例如,Jack wonJill won)。但情況并非如此。完全有可能在這段代碼之后,內容現在是Jack wonJill won。
'內部同步'僅僅意味著在使用StringBuilder時,理論上你可以以Jaicllk wwonon結束,因為兩個追加呼叫同時運行,并使這一切變得混亂,而在使用StringBuffer時,這種情況不可能發生 - 任何單一方法呼叫到StringBuffer都會阻止其他方法呼叫到它。
換句話說,這個if塊的問題在于你有2個方法呼叫(在if條件中的toString(),然后在主體中的append),而StringBuffer并不能使之作業,并且沒有任何StringBuffer的實作可以使之作業。
這個問題有2種解決方案:
[1]使用鎖來解決這個問題。
[1] 明確地使用鎖
synchronized (strBuf) {
if (strBuf.toString().isEmpty()) strBuf.append("Jill won"/span>)。
}
效果很好。這樣做,你實際上不再需要內部同步方面的問題了。你已經處理好了這個問題。
[2] 制定單一的方法來完成整個作業
這將是針對StringBuffer的作者的,你無法解決這個問題。但是,他們可以有一個方法來命名,例如appendIfEmpty,然后你可以寫strBuffer.appendIfEmpty("Jill won")/code>。現在它是一個單一的方法,StringBuffer的代碼可以處理業務,并確保它是原子的/同步的。
查看HashMap的javadoc,其中有一堆這樣的 "原子 "方法,例如putIfAbsent。
關于內部同步和StringBuffer/StringBuilder的最后思考
。
如果沒有同步,你根本無法得到任何保證。代碼基本上可以做任何事情。最終在緩沖區中出現Jaicllk wwoonn甚至不是最糟糕的情況。例外也可能發生,就像一個處于無效狀態的StringBuilder(它報告說它是,例如,大小為9,但當列印時卻更大,或者它看起來很好,但當你對它呼叫toString()時,就發生例外,等等)。在這個意義上,StringBuffer是 "更安全的",除了這沒有用,因為做任意事情的代碼是沒有用的。幾乎所有存在的 API,無論是 Map、List 還是 StringBuilder,都適合于 "復合 "操作,在這種情況下,您需要呼叫至少兩個方法來完成作業,這時,內部同步是完全沒用的。
這意味著內部同步(這就是StringBuffer、Vector、Hashtable和Collections. synchronizedList/Map/Set給你)幾乎是完全沒有意義的,這就是為什么JVM已經開始遠離它,使ArrayList、HashSet和co不再內部同步,并使StringBuilder。
就其價值而言,自從引入例如StringBuilder后,虛擬機已經得到了改進。現在,在沒有其他執行緒的情況下鎖定一個物件基本上是免費的,或者至少對性能的影響是無限大的。因此,在使用StringBuffer時,如果使用StringBuilder也能達到同樣的效果,就不再是一個顯著的性能損失。
因此,忘記StringBuffer而只使用StringBuilder的理由如下:
- 認為StringBuffer已經過時了因為它是。java核心庫不會被標記為廢棄或洗掉,即使是過時的。參見Vector, Hashtable, java.util.Date等。他們就是不這樣做--openjdk開發團隊認為 "10年前寫的代碼在今天的javac上編譯時最好仍能編譯,并做同樣的事情 "如此重要,所以他們不這樣做。所以不要犯這樣的錯誤。"哦,StringBuffer是一個存在的東西,而且沒有被明確地標記為過時,因此它一定有什么意義"。不,沒有。永遠不要使用它。
- 內部同步幾乎是完全無用的,請看你的代碼片段,它并沒有做你所期望/想要的事情。
- 整個java社區都在使用StringBuilder,無處不在。當有兩種不同的方式來做同樣的事情,并且它們在所需的努力、性能和代碼風格方面似乎是相同的(就像這里),你應該做社區喜歡做的事情。它降低了團隊的學習曲線,避免了令人討厭的風格差異(你用一種方式寫,你的朋友用另一種方式寫,每次你讀到對方的代碼時,都會無緣無故地感到奇怪),而且走好這條路,你就能得到與其他庫中的bug有關的較少問題的代碼,與更多的庫一起作業,并獲得性能上的好處,因為OpenJDK的工程師們致力于使java快速發展,關注常見的代碼模式。稱之為在羅馬時,像羅馬人一樣行事規則。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/325317.html
標籤:
上一篇:多執行緒點擊宏/點擊記錄器
