日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

再有人问你synchronized是什么,就把这篇文章发给他。

發(fā)布時(shí)間:2025/3/20 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 再有人问你synchronized是什么,就把这篇文章发给他。 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在《深入理解Java虛擬機(jī)》中,有這樣一段話(huà):

synchronized關(guān)鍵字在需要原子性、可見(jiàn)性和有序性這三種特性的時(shí)候都可以作為其中一種解決方案,看起來(lái)是“萬(wàn)能”的。的確,大部分并發(fā)控制操作都能使用synchronized來(lái)完成。

海明威在他的《午后之死》說(shuō)過(guò)的:“冰山運(yùn)動(dòng)之雄偉壯觀(guān),是因?yàn)樗挥邪朔种辉谒嫔稀!?/p>

對(duì)于程序員來(lái)說(shuō),synchronized只是個(gè)關(guān)鍵字而已,用起來(lái)很簡(jiǎn)單。之所以我們可以在處理多線(xiàn)程問(wèn)題時(shí)可以不用考慮太多,就是因?yàn)檫@個(gè)關(guān)鍵字幫我們屏蔽了很多細(xì)節(jié)。

那么,本文就圍繞synchronized展開(kāi),主要介紹其用法、原理,以及如何提供原子性、可見(jiàn)性和有序性保障的等。

synchronized的用法

synchronized是Java提供的一個(gè)并發(fā)控制的關(guān)鍵字。主要有兩種用法,分別是同步方法和同步代碼塊。

也就是說(shuō),synchronized既可以修飾方法也可以修飾代碼塊。代碼如下:

/**

*?@author?Hollis?18/08/04.

*/

publicclassSynchronizedDemo{

//同步方法

publicsynchronizedvoiddoSth(){

System.out.println("Hello?World");

}

//同步代碼塊

publicvoiddoSth1(){

synchronized?(SynchronizedDemo.class){

System.out.println("Hello?World");

}

}

}

被synchronized修飾的代碼塊及方法,在同一時(shí)間,只能被單個(gè)線(xiàn)程訪(fǎng)問(wèn)。

synchronized的實(shí)現(xiàn)原理

synchronized,是Java中用于解決并發(fā)情況下數(shù)據(jù)同步訪(fǎng)問(wèn)的一個(gè)很重要的關(guān)鍵字。當(dāng)我們想要保證一個(gè)共享資源在同一時(shí)間只會(huì)被一個(gè)線(xiàn)程訪(fǎng)問(wèn)到時(shí),我們可以在代碼中使用synchronized關(guān)鍵字對(duì)類(lèi)或者對(duì)象加鎖。

在深入理解多線(xiàn)程(一)——Synchronized的實(shí)現(xiàn)原理中我曾經(jīng)介紹過(guò)其實(shí)現(xiàn)原理,為了保證知識(shí)的完整性,這里再簡(jiǎn)單介紹一下,詳細(xì)的內(nèi)容請(qǐng)去原文閱讀。

我們對(duì)上面的代碼進(jìn)行反編譯,可以得到如下代碼:

publicsynchronizedvoiddoSth();

descriptor:?()V

flags:?ACC_PUBLIC,?ACC_SYNCHRONIZED

Code:

stack=2,?locals=1,?args_size=1

0:?getstatic?????#2//?Field?java/lang/System.out:Ljava/io/PrintStream;

3:?ldc???????????#3//?String?Hello?World

5:?invokevirtual?#4//?Method?java/io/PrintStream.println:(Ljava/lang/String;)V

8:return

publicvoiddoSth1();

descriptor:?()V

flags:?ACC_PUBLIC

Code:

stack=2,?locals=3,?args_size=1

0:?ldc???????????#5//?class?com/hollis/SynchronizedTest

2:?dup

3:?astore_1

4:?monitorenter

5:?getstatic?????#2//?Field?java/lang/System.out:Ljava/io/PrintStream;

8:?ldc???????????#3//?String?Hello?World

10:?invokevirtual?#4//?Method?java/io/PrintStream.println:(Ljava/lang/String;)V

13:?aload_1

14:?monitorexit

15:goto23

18:?astore_2

19:?aload_1

20:?monitorexit

21:?aload_2

22:?athrow

23:return

通過(guò)反編譯后代碼可以看出:

對(duì)于同步方法,JVM采用ACC_SYNCHRONIZED標(biāo)記符來(lái)實(shí)現(xiàn)同步。

對(duì)于同步代碼塊。JVM采用monitorenter、monitorexit兩個(gè)指令來(lái)實(shí)現(xiàn)同步。

在The Java? Virtual Machine Specification中有關(guān)于同步方法和同步代碼塊的實(shí)現(xiàn)原理的介紹,我翻譯成中文如下:

方法級(jí)的同步是隱式的。同步方法的常量池中會(huì)有一個(gè)ACC_SYNCHRONIZED標(biāo)志。當(dāng)某個(gè)線(xiàn)程要訪(fǎng)問(wèn)某個(gè)方法的時(shí)候,會(huì)檢查是否有ACC_SYNCHRONIZED,如果有設(shè)置,則需要先獲得監(jiān)視器鎖,然后開(kāi)始執(zhí)行方法,方法執(zhí)行之后再釋放監(jiān)視器鎖。這時(shí)如果其他線(xiàn)程來(lái)請(qǐng)求執(zhí)行方法,會(huì)因?yàn)闊o(wú)法獲得監(jiān)視器鎖而被阻斷住。值得注意的是,如果在方法執(zhí)行過(guò)程中,發(fā)生了異常,并且方法內(nèi)部并沒(méi)有處理該異常,那么在異常被拋到方法外面之前監(jiān)視器鎖會(huì)被自動(dòng)釋放。

同步代碼塊使用monitorenter和monitorexit兩個(gè)指令實(shí)現(xiàn)。可以把執(zhí)行monitorenter指令理解為加鎖,執(zhí)行monitorexit理解為釋放鎖。 每個(gè)對(duì)象維護(hù)著一個(gè)記錄著被鎖次數(shù)的計(jì)數(shù)器。未被鎖定的對(duì)象的該計(jì)數(shù)器為0,當(dāng)一個(gè)線(xiàn)程獲得鎖(執(zhí)行monitorenter)后,該計(jì)數(shù)器自增變?yōu)?1 ,當(dāng)同一個(gè)線(xiàn)程再次獲得該對(duì)象的鎖的時(shí)候,計(jì)數(shù)器再次自增。當(dāng)同一個(gè)線(xiàn)程釋放鎖(執(zhí)行monitorexit指令)的時(shí)候,計(jì)數(shù)器再自減。當(dāng)計(jì)數(shù)器為0的時(shí)候。鎖將被釋放,其他線(xiàn)程便可以獲得鎖。

無(wú)論是ACC_SYNCHRONIZED還是monitorenter、monitorexit都是基于Monitor實(shí)現(xiàn)的,在Java虛擬機(jī)(HotSpot)中,Monitor是基于C++實(shí)現(xiàn)的,由ObjectMonitor實(shí)現(xiàn)。

ObjectMonitor類(lèi)中提供了幾個(gè)方法,如enter、exit、wait、notify、notifyAll等。sychronized加鎖的時(shí)候,會(huì)調(diào)用objectMonitor的enter方法,解鎖的時(shí)候會(huì)調(diào)用exit方法。(關(guān)于Monitor詳見(jiàn)深入理解多線(xiàn)程(四)—— Moniter的實(shí)現(xiàn)原理)

synchronized與原子性

原子性是指一個(gè)操作是不可中斷的,要全部執(zhí)行完成,要不就都不執(zhí)行。

我們?cè)贘ava的并發(fā)編程中的多線(xiàn)程問(wèn)題到底是怎么回事兒中分析過(guò):

線(xiàn)程是CPU調(diào)度的基本單位。CPU有時(shí)間片的概念,會(huì)根據(jù)不同的調(diào)度算法進(jìn)行線(xiàn)程調(diào)度。當(dāng)一個(gè)線(xiàn)程獲得時(shí)間片之后開(kāi)始執(zhí)行,在時(shí)間片耗盡之后,就會(huì)失去CPU使用權(quán)。所以在多線(xiàn)程場(chǎng)景下,由于時(shí)間片在線(xiàn)程間輪換,就會(huì)發(fā)生原子性問(wèn)題。

在Java中,為了保證原子性,提供了兩個(gè)高級(jí)的字節(jié)碼指令monitorenter和monitorexit。

前面介紹過(guò),這兩個(gè)字節(jié)碼指令,在Java中對(duì)應(yīng)的關(guān)鍵字就是synchronized。

通過(guò)monitorenter和monitorexit指令,可以保證被synchronized修飾的代碼在同一時(shí)間只能被一個(gè)線(xiàn)程訪(fǎng)問(wèn),在鎖未釋放之前,無(wú)法被其他線(xiàn)程訪(fǎng)問(wèn)到。

因此,在Java中可以使用synchronized來(lái)保證方法和代碼塊內(nèi)的操作是原子性的。

線(xiàn)程1在執(zhí)行monitorenter指令的時(shí)候,會(huì)對(duì)Monitor進(jìn)行加鎖,加鎖后其他線(xiàn)程無(wú)法獲得鎖,除非線(xiàn)程1主動(dòng)解鎖。即使在執(zhí)行過(guò)程中,由于某種原因,比如CPU時(shí)間片用完,線(xiàn)程1放棄了CPU,但是,他并沒(méi)有進(jìn)行解鎖。而由于synchronized的鎖是可重入的,下一個(gè)時(shí)間片還是只能被他自己獲取到,還是會(huì)繼續(xù)執(zhí)行代碼。直到所有代碼執(zhí)行完。這就保證了原子性。

synchronized與可見(jiàn)性

可見(jiàn)性是指當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)變量時(shí),一個(gè)線(xiàn)程修改了這個(gè)變量的值,其他線(xiàn)程能夠立即看得到修改的值。

我們?cè)谠儆腥藛?wèn)你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過(guò):

Java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存中,每條線(xiàn)程還有自己的工作內(nèi)存,線(xiàn)程的工作內(nèi)存中保存了該線(xiàn)程中是用到的變量的主內(nèi)存副本拷貝,線(xiàn)程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫(xiě)主內(nèi)存。不同的線(xiàn)程之間也無(wú)法直接訪(fǎng)問(wèn)對(duì)方工作內(nèi)存中的變量,線(xiàn)程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步進(jìn)行。所以,就可能出現(xiàn)線(xiàn)程1改了某個(gè)變量的值,但是線(xiàn)程2不可見(jiàn)的情況。

前面我們介紹過(guò),被synchronized修飾的代碼,在開(kāi)始執(zhí)行時(shí)會(huì)加鎖,執(zhí)行完成后會(huì)進(jìn)行解鎖。

而為了保證可見(jiàn)性,有一條規(guī)則是這樣的:對(duì)一個(gè)變量解鎖之前,必須先把此變量同步回主存中。這樣解鎖后,后續(xù)線(xiàn)程就可以訪(fǎng)問(wèn)到被修改后的值。

所以,synchronized關(guān)鍵字鎖住的對(duì)象,其值是具有可見(jiàn)性的。

synchronized與有序性

有序性即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

我們?cè)谠儆腥藛?wèn)你Java內(nèi)存模型是什么,就把這篇文章發(fā)給他中分析過(guò):

除了引入了時(shí)間片以外,由于處理器優(yōu)化和指令重排等,CPU還可能對(duì)輸入代碼進(jìn)行亂序執(zhí)行,比如load->add->save 有可能被優(yōu)化成load->save->add 。這就是可能存在有序性問(wèn)題。

這里需要注意的是,synchronized是無(wú)法禁止指令重排和處理器優(yōu)化的。也就是說(shuō),synchronized無(wú)法避免上述提到的問(wèn)題。

那么,為什么還說(shuō)synchronized也提供了有序性保證呢?

這就要再把有序性的概念擴(kuò)展一下了。

Java程序中天然的有序性可以總結(jié)為一句話(huà):如果在本線(xiàn)程內(nèi)觀(guān)察,所有操作都是天然有序的。如果在一個(gè)線(xiàn)程中觀(guān)察另一個(gè)線(xiàn)程,所有操作都是無(wú)序的。

以上這句話(huà)也是《深入理解Java虛擬機(jī)》中的原句,但是怎么理解呢?周志明并沒(méi)有詳細(xì)的解釋。這里我簡(jiǎn)單擴(kuò)展一下,這其實(shí)和as-if-serial語(yǔ)義有關(guān)。

as-if-serial語(yǔ)義的意思指:不管怎么重排序(編譯器和處理器為了提高并行度),單線(xiàn)程程序的執(zhí)行結(jié)果都不能被改變。編譯器和處理器無(wú)論如何優(yōu)化,都必須遵守as-if-serial語(yǔ)義。

這里不對(duì)as-if-serial語(yǔ)義詳細(xì)展開(kāi)了,簡(jiǎn)單說(shuō)就是,as-if-serial語(yǔ)義保證了單線(xiàn)程中,指令重排是有一定的限制的,而只要編譯器和處理器都遵守了這個(gè)語(yǔ)義,那么就可以認(rèn)為單線(xiàn)程程序是按照順序執(zhí)行的。當(dāng)然,實(shí)際上還是有重排的,只不過(guò)我們無(wú)須關(guān)心這種重排的干擾。

所以呢,由于synchronized修飾的代碼,同一時(shí)間只能被同一線(xiàn)程訪(fǎng)問(wèn)。那么也就是單線(xiàn)程執(zhí)行的。所以,可以保證其有序性。

synchronized與鎖優(yōu)化

前面介紹了synchronized的用法、原理以及對(duì)并發(fā)編程的作用。是一個(gè)很好用的關(guān)鍵字。

synchronized其實(shí)是借助Monitor實(shí)現(xiàn)的,在加鎖時(shí)會(huì)調(diào)用objectMonitor的enter方法,解鎖的時(shí)候會(huì)調(diào)用exit方法。事實(shí)上,只有在JDK1.6之前,synchronized的實(shí)現(xiàn)才會(huì)直接調(diào)用ObjectMonitor的enter和exit,這種鎖被稱(chēng)之為重量級(jí)鎖。

所以,在JDK1.6中出現(xiàn)對(duì)鎖進(jìn)行了很多的優(yōu)化,進(jìn)而出現(xiàn)輕量級(jí)鎖,偏向鎖,鎖消除,適應(yīng)性自旋鎖,鎖粗化(自旋鎖在1.4就有,只不過(guò)默認(rèn)的是關(guān)閉的,jdk1.6是默認(rèn)開(kāi)啟的),這些操作都是為了在線(xiàn)程之間更高效的共享數(shù)據(jù) ,解決競(jìng)爭(zhēng)問(wèn)題。

關(guān)于自旋鎖、鎖粗化和鎖消除可以參考深入理解多線(xiàn)程(五)—— Java虛擬機(jī)的鎖優(yōu)化技術(shù),關(guān)于輕量級(jí)鎖和偏向鎖,已經(jīng)在排期規(guī)劃中,我后面會(huì)有文章單獨(dú)介紹,將獨(dú)家發(fā)布在我的博客(http://www.hollischuang.com)和公眾號(hào)(Hollis)中,敬請(qǐng)期待。

好啦,關(guān)于synchronized關(guān)鍵字,我們介紹了其用法、原理、以及如何保證的原子性、順序性和可見(jiàn)性,同時(shí)也擴(kuò)展的留下了鎖優(yōu)化相關(guān)的資料及思考。后面我們會(huì)繼續(xù)介紹volatile關(guān)鍵字以及他和synchronized的區(qū)別等。敬請(qǐng)期待。

歡迎工作一到五年的Java工程師朋友們加入Java架構(gòu)開(kāi)發(fā):860113481

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來(lái)學(xué)習(xí)提升自己,不要再用"沒(méi)有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來(lái)的自己一個(gè)交代!

總結(jié)

以上是生活随笔為你收集整理的再有人问你synchronized是什么,就把这篇文章发给他。的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。