Java StringBuffer與StringBuilder有什么區(qū)別
一問道StringBuffer與StringBuilder的區(qū)別,張口就來StringBuffer是線程安全的,因?yàn)樗嚓P(guān)方法都加了synchronized 關(guān)鍵字,StringBuilder線程不安全。沒錯(cuò),確實(shí)如此,但是我們查看過源碼會(huì)發(fā)現(xiàn)StringBuffer是從jdk1.0就開始了,StringBuilder是從jdk1.5開始的。于是我就產(chǎn)生這樣一個(gè)疑問,既然已經(jīng)有了StringBuffer,為什么jdk5又出了一個(gè)StringBuilder呢,也就是單線程時(shí)候StringBuffer與StringBuilder有什么區(qū)別。
一、StringBuffer與StringBuilder的共同之處
1、都繼成了AbstractStringBuilder這個(gè)抽象類,實(shí)現(xiàn)了CharSequence接口
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence
2、其append方法都是 super.append(str),調(diào)用了父類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和擴(kuò)容機(jī)制都是"舊容量*2+2"
4、底層都是用char[]字符數(shù)組實(shí)現(xiàn),且字符數(shù)組都是可變的,這點(diǎn)不同于String
二、StringBuffer與StringBuilder的不同之處
- StringBuffer多線程安全的,StringBuilder多線程不安全
- StringBuffer從JDK1.0就有了,StringBuilder是JDK5.0才出現(xiàn)
- StringBuffer比StringBuilder多了一個(gè)toStringCache字段,用來在toString方法中進(jìn)行緩存,每次append操作之前都先把toStringCache設(shè)置為null,若多次連續(xù)調(diào)用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ū)別
這個(gè)才是我們重點(diǎn)討論的,單線程下StringBuffer加了synchronized,雖然是單線程, 但是synchronized獲取鎖和釋放鎖也還是需要時(shí)間的, 而StringBuilder沒有,這個(gè)就是重點(diǎn)區(qū)別。因此重點(diǎn)要討論synchronized鎖的狀態(tài),從獲取鎖到釋放鎖的過程,因此需要討論一下鎖的升級(jí)和優(yōu)化。
鎖的4中狀態(tài):無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)、重量級(jí)鎖狀態(tài)(級(jí)別從低到高)
(1)偏向鎖:
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問,那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)。
為什么要引入偏向鎖?
因?yàn)榻?jīng)過HotSpot的作者大量的研究發(fā)現(xiàn),大多數(shù)時(shí)候是不存在鎖競爭的,常常是一個(gè)線程多次獲得同一個(gè)鎖,因此如果每次都要競爭鎖會(huì)增大很多沒有必要付出的代價(jià),為了降低獲取鎖的代價(jià),才引入的偏向鎖。
偏向鎖的升級(jí)
當(dāng)線程1訪問代碼塊并獲取鎖對(duì)象時(shí),會(huì)在java對(duì)象頭和棧幀中記錄偏向的鎖的threadID,因?yàn)槠蜴i不會(huì)主動(dòng)釋放鎖,因此以后線程1再次獲取鎖的時(shí)候,需要比較當(dāng)前線程的threadID和Java對(duì)象頭中的threadID是否一致,如果一致(還是線程1獲取鎖對(duì)象),則無需使用CAS來加鎖、解鎖;如果不一致(其他線程,如線程2要競爭鎖對(duì)象,而偏向鎖不會(huì)主動(dòng)釋放因此還是存儲(chǔ)的線程1的threadID),那么需要查看Java對(duì)象頭中記錄的線程1是否存活,如果沒有存活,那么鎖對(duì)象被重置為無鎖狀態(tài),其它線程(線程2)可以競爭將其設(shè)置為偏向鎖;如果存活,那么立刻查找該線程(線程1)的棧幀信息,如果還是需要繼續(xù)持有這個(gè)鎖對(duì)象,那么暫停當(dāng)前線程1,撤銷偏向鎖,升級(jí)為輕量級(jí)鎖,如果線程1 不再使用該鎖對(duì)象,那么將鎖對(duì)象狀態(tài)設(shè)為無鎖狀態(tài),重新偏向新的線程。
偏向鎖的取消:
偏向鎖是默認(rèn)開啟的,而且開始時(shí)間一般是比應(yīng)用程序啟動(dòng)慢幾秒,如果不想有這個(gè)延遲,那么可以使用-XX:BiasedLockingStartUpDelay=0;
如果不想要偏向鎖,那么可以通過-XX:-UseBiasedLocking = false來設(shè)置;
(2)輕量級(jí)鎖
輕量級(jí)鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過自旋的形式嘗試獲取鎖,不會(huì)阻塞,提高性能
為什么要引入輕量級(jí)鎖?
輕量級(jí)鎖考慮的是競爭鎖對(duì)象的線程不多,而且線程持有鎖的時(shí)間也不長的情景。因?yàn)樽枞€程需要CPU從用戶態(tài)轉(zhuǎn)到內(nèi)核態(tài),代價(jià)較大,如果剛剛阻塞不久這個(gè)鎖就被釋放了,那這個(gè)代價(jià)就有點(diǎn)得不償失了,因此這個(gè)時(shí)候就干脆不阻塞這個(gè)線程,讓它自旋這等待鎖釋放。
(3)重量級(jí)鎖
重量級(jí)鎖是指當(dāng)鎖為輕量級(jí)鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會(huì)一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時(shí)候,還沒有獲取到鎖,就會(huì)進(jìn)入阻塞,該鎖膨脹為重量級(jí)鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。
輕量級(jí)鎖什么時(shí)候升級(jí)為重量級(jí)鎖?
線程1獲取輕量級(jí)鎖時(shí)會(huì)先把鎖對(duì)象的對(duì)象頭MarkWord復(fù)制一份到線程1的棧幀中創(chuàng)建的用于存儲(chǔ)鎖記錄的空間(稱為DisplacedMarkWord),然后使用CAS把對(duì)象頭中的內(nèi)容替換為線程1存儲(chǔ)的鎖記錄(DisplacedMarkWord)的地址;
如果在線程1復(fù)制對(duì)象頭的同時(shí)(在線程1CAS之前),線程2也準(zhǔn)備獲取鎖,復(fù)制了對(duì)象頭到線程2的鎖記錄空間中,但是在線程2CAS的時(shí)候,發(fā)現(xiàn)線程1已經(jīng)把對(duì)象頭換了,線程2的CAS失敗,那么線程2就嘗試使用自旋鎖來等待線程1釋放鎖。
但是如果自旋的時(shí)間太長也不行,因?yàn)樽孕且腃PU的,因此自旋的次數(shù)是有限制的,比如10次或者100次,如果自旋次數(shù)到了線程1還沒有釋放鎖,或者線程1還在執(zhí)行,線程2還在自旋等待,這時(shí)又有一個(gè)線程3過來競爭這個(gè)鎖對(duì)象,那么這個(gè)時(shí)候輕量級(jí)鎖就會(huì)膨脹為重量級(jí)鎖。重量級(jí)鎖把除了擁有鎖的線程都阻塞,防止CPU空轉(zhuǎn)。
*注意:為了避免無用的自旋,輕量級(jí)鎖一旦膨脹為重量級(jí)鎖就不會(huì)再降級(jí)為輕量級(jí)鎖了;偏向鎖升級(jí)為輕量級(jí)鎖也不能再降級(jí)為偏向鎖。一句話就是鎖可以升級(jí)不可以降級(jí),但是偏向鎖狀態(tài)可以被重置為無鎖狀態(tài)。
綜上可知,StringBuffer雖然是單線程,但它是有偏向鎖升級(jí)過程判斷的,會(huì)耗費(fèi)時(shí)間,效率固然低于StringBuilder
四、StringBuffer與StringBuilder的應(yīng)用場景
1、StringBuffer多線程安全,但是加了synchronized,其效率低。故適用于多線程下,并發(fā)量不是很高的場景
2、StringBuilder沒有加任何鎖,其效率高,適用單線程場景,但同時(shí)也適用于高并發(fā)場景中,提高高并發(fā)場景下程序的響應(yīng)性能,至于線程安全問題可以通過其它手段解決,如ThreadLocal,CAS操作等。
3、所以對(duì)于高并發(fā)場景下,若有用到二者,還是建議優(yōu)先使用StringBuilder的
到此這篇關(guān)于Java StringBuffer與StringBuilder有什么區(qū)別的文章就介紹到這了,更多相關(guān)Java StringBuffer與StringBuilder內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 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詳細(xì)分析String類與StringBuffer和StringBuilder的使用方法
- Java中String、StringBuffer和StringBuilder的區(qū)別與使用場景
相關(guān)文章
詳解SpringBoot如何優(yōu)雅的進(jìn)行前后端通信
現(xiàn)在的項(xiàng)目基本上都是前后端分離的項(xiàng)目,如何打通前后端,接收前端傳過來的參數(shù)呢,下面小編就來和大家詳細(xì)介紹一下SpringBoot如何優(yōu)雅的進(jìn)行前后端通信2024-03-03springboot項(xiàng)目(jar包)指定配置文件啟動(dòng)圖文教程
這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目(jar包)指定配置文件啟動(dòng)的相關(guān)資料,在多環(huán)境部署過程中、及線上運(yùn)維中可能會(huì)遇到臨時(shí)指定配置文件的情況,需要的朋友可以參考下2023-07-07關(guān)于JAVA8的 Stream學(xué)習(xí)
這篇文章主要介紹了JAVA8 Stream學(xué)習(xí)方法的相關(guān)資料,需要的朋友可以參考下面文章內(nèi)容2021-09-09解決spring-boot2.0.6中webflux無法獲得請(qǐng)求IP的問題
這幾天在用 spring-boot 2 的 webflux 重構(gòu)一個(gè)工程,寫到了一個(gè)需要獲得客戶端請(qǐng)求 IP 的地方,在寫的過程中遇到很多問題,下面小編通過一段代碼給大家介紹解決spring-boot2.0.6中webflux無法獲得請(qǐng)求IP的問題,感興趣的朋友跟隨小編一起看看吧2018-10-10Android 應(yīng)用按返回鍵退向后臺(tái)運(yùn)行實(shí)例代碼
這篇文章主要介紹了Android 應(yīng)用按返回鍵退向后臺(tái)運(yùn)行實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04IntelliJ IDEA 2020.3 重大特性(新功能一覽)
這篇文章主要介紹了IntelliJ IDEA 2020.3 重大特性(新功能一覽),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12java應(yīng)用開發(fā)之Mybatis通過Mapper代理自定義接口的實(shí)現(xiàn)
這篇文章主要介紹了java應(yīng)用開發(fā)之Mybatis通過Mapper代理自定義接口的實(shí)現(xiàn)方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09