Java StringBuffer與StringBuilder有什么區(qū)別
一問道StringBuffer與StringBuilder的區(qū)別,張口就來StringBuffer是線程安全的,因為它相關方法都加了synchronized 關鍵字,StringBuilder線程不安全。沒錯,確實如此,但是我們查看過源碼會發(fā)現(xiàn)StringBuffer是從jdk1.0就開始了,StringBuilder是從jdk1.5開始的。于是我就產生這樣一個疑問,既然已經有了StringBuffer,為什么jdk5又出了一個StringBuilder呢,也就是單線程時候StringBuffer與StringBuilder有什么區(qū)別。
一、StringBuffer與StringBuilder的共同之處
1、都繼成了AbstractStringBuilder這個抽象類,實現(xiàn)了CharSequence接口
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence2、其append方法都是 super.append(str),調用了父類AbstractStringBuilder的append(String str)方法
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}3、初始容量都是16和擴容機制都是"舊容量*2+2"
4、底層都是用char[]字符數(shù)組實現(xiàn),且字符數(shù)組都是可變的,這點不同于String
二、StringBuffer與StringBuilder的不同之處
- StringBuffer多線程安全的,StringBuilder多線程不安全
- StringBuffer從JDK1.0就有了,StringBuilder是JDK5.0才出現(xiàn)
- StringBuffer比StringBuilder多了一個toStringCache字段,用來在toString方法中進行緩存,每次append操作之前都先把toStringCache設置為null,若多次連續(xù)調用toString方法,可避免每次Arrays.copyOfRange(value, 0, count)操作,節(jié)省性能。
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}4、由于StringBuilder沒有考慮同步,在單線程情況下,StringBuilder的性能要優(yōu)于StringBuffer
三、單線程StringBuffer與StringBuilder區(qū)別
這個才是我們重點討論的,單線程下StringBuffer加了synchronized,雖然是單線程, 但是synchronized獲取鎖和釋放鎖也還是需要時間的, 而StringBuilder沒有,這個就是重點區(qū)別。因此重點要討論synchronized鎖的狀態(tài),從獲取鎖到釋放鎖的過程,因此需要討論一下鎖的升級和優(yōu)化。
鎖的4中狀態(tài):無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)、重量級鎖狀態(tài)(級別從低到高)
(1)偏向鎖:
偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖。降低獲取鎖的代價。
為什么要引入偏向鎖?
因為經過HotSpot的作者大量的研究發(fā)現(xiàn),大多數(shù)時候是不存在鎖競爭的,常常是一個線程多次獲得同一個鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,為了降低獲取鎖的代價,才引入的偏向鎖。
偏向鎖的升級
當線程1訪問代碼塊并獲取鎖對象時,會在java對象頭和棧幀中記錄偏向的鎖的threadID,因為偏向鎖不會主動釋放鎖,因此以后線程1再次獲取鎖的時候,需要比較當前線程的threadID和Java對象頭中的threadID是否一致,如果一致(還是線程1獲取鎖對象),則無需使用CAS來加鎖、解鎖;如果不一致(其他線程,如線程2要競爭鎖對象,而偏向鎖不會主動釋放因此還是存儲的線程1的threadID),那么需要查看Java對象頭中記錄的線程1是否存活,如果沒有存活,那么鎖對象被重置為無鎖狀態(tài),其它線程(線程2)可以競爭將其設置為偏向鎖;如果存活,那么立刻查找該線程(線程1)的棧幀信息,如果還是需要繼續(xù)持有這個鎖對象,那么暫停當前線程1,撤銷偏向鎖,升級為輕量級鎖,如果線程1 不再使用該鎖對象,那么將鎖對象狀態(tài)設為無鎖狀態(tài),重新偏向新的線程。
偏向鎖的取消:
偏向鎖是默認開啟的,而且開始時間一般是比應用程序啟動慢幾秒,如果不想有這個延遲,那么可以使用-XX:BiasedLockingStartUpDelay=0;
如果不想要偏向鎖,那么可以通過-XX:-UseBiasedLocking = false來設置;
(2)輕量級鎖
輕量級鎖是指當鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能
為什么要引入輕量級鎖?
輕量級鎖考慮的是競爭鎖對象的線程不多,而且線程持有鎖的時間也不長的情景。因為阻塞線程需要CPU從用戶態(tài)轉到內核態(tài),代價較大,如果剛剛阻塞不久這個鎖就被釋放了,那這個代價就有點得不償失了,因此這個時候就干脆不阻塞這個線程,讓它自旋這等待鎖釋放。
(3)重量級鎖
重量級鎖是指當鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續(xù)下去,當自旋一定次數(shù)的時候,還沒有獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進入阻塞,性能降低。
輕量級鎖什么時候升級為重量級鎖?
線程1獲取輕量級鎖時會先把鎖對象的對象頭MarkWord復制一份到線程1的棧幀中創(chuàng)建的用于存儲鎖記錄的空間(稱為DisplacedMarkWord),然后使用CAS把對象頭中的內容替換為線程1存儲的鎖記錄(DisplacedMarkWord)的地址;
如果在線程1復制對象頭的同時(在線程1CAS之前),線程2也準備獲取鎖,復制了對象頭到線程2的鎖記錄空間中,但是在線程2CAS的時候,發(fā)現(xiàn)線程1已經把對象頭換了,線程2的CAS失敗,那么線程2就嘗試使用自旋鎖來等待線程1釋放鎖。
但是如果自旋的時間太長也不行,因為自旋是要消耗CPU的,因此自旋的次數(shù)是有限制的,比如10次或者100次,如果自旋次數(shù)到了線程1還沒有釋放鎖,或者線程1還在執(zhí)行,線程2還在自旋等待,這時又有一個線程3過來競爭這個鎖對象,那么這個時候輕量級鎖就會膨脹為重量級鎖。重量級鎖把除了擁有鎖的線程都阻塞,防止CPU空轉。
*注意:為了避免無用的自旋,輕量級鎖一旦膨脹為重量級鎖就不會再降級為輕量級鎖了;偏向鎖升級為輕量級鎖也不能再降級為偏向鎖。一句話就是鎖可以升級不可以降級,但是偏向鎖狀態(tài)可以被重置為無鎖狀態(tài)。
綜上可知,StringBuffer雖然是單線程,但它是有偏向鎖升級過程判斷的,會耗費時間,效率固然低于StringBuilder
四、StringBuffer與StringBuilder的應用場景
1、StringBuffer多線程安全,但是加了synchronized,其效率低。故適用于多線程下,并發(fā)量不是很高的場景
2、StringBuilder沒有加任何鎖,其效率高,適用單線程場景,但同時也適用于高并發(fā)場景中,提高高并發(fā)場景下程序的響應性能,至于線程安全問題可以通過其它手段解決,如ThreadLocal,CAS操作等。
3、所以對于高并發(fā)場景下,若有用到二者,還是建議優(yōu)先使用StringBuilder的
到此這篇關于Java StringBuffer與StringBuilder有什么區(qū)別的文章就介紹到這了,更多相關Java StringBuffer與StringBuilder內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Java中String、StringBuffer和StringBuilder的區(qū)別
- Java中的String、StringBuilder、StringBuffer三者的區(qū)別詳解
- Java中StringBuilder與StringBuffer的區(qū)別
- Java中StringBuilder與StringBuffer使用及源碼解讀
- 淺析Java中StringBuffer和StringBuilder的使用
- 詳解Java中String,StringBuffer和StringBuilder的使用
- Java中String和StringBuffer及StringBuilder?有什么區(qū)別
- Java源碼深度分析String與StringBuffer及StringBuilder詳解
- Java詳細分析String類與StringBuffer和StringBuilder的使用方法
- Java中String、StringBuffer和StringBuilder的區(qū)別與使用場景
相關文章
SpringBoot Controller Post接口單元測試示例
今天小編就為大家分享一篇關于SpringBoot Controller Post接口單元測試示例,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
Java 中的FileReader和FileWriter源碼分析_動力節(jié)點Java學院整理
本文給大家分享一段示例程序,通過示例代碼可以看出FileReader是基于InputStreamReader實現(xiàn)的,FileWriter是基于OutputStreamWriter實現(xiàn)的,具體程序代碼大家通過本文了解下吧2017-05-05
Java web Hibernate如何與數(shù)據(jù)庫鏈接
這篇文章主要介紹了Java web Hibernate如何與數(shù)據(jù)庫鏈接,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06
Java實戰(zhàn)之OutOfMemoryError異常問題及解決方法
這篇文章主要介紹了Java實戰(zhàn)之OutOfMemoryError異常,主要結合著深入理解Java虛擬機一書當中整理了本篇內容,感興趣的朋友一起看看吧2022-04-04
解決springboot+thymeleaf視圖映射報錯There?was?an?unexpected?erro
這篇文章主要介紹了解決springboot+thymeleaf視圖映射報錯There?was?an?unexpected?error?(type=Not?Found,?status=404)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

