Java關(guān)鍵字synchronized基本使用詳解
基本使用
Java中的synchronized關(guān)鍵字用于在多線程環(huán)境下確保數(shù)據(jù)同步。它可以用來(lái)修飾方法和代碼塊
當(dāng)一個(gè)線程訪問(wèn)一個(gè)對(duì)象的synchronized方法或代碼塊時(shí),其他線程將無(wú)法訪問(wèn)該對(duì)象的其他synchronized方法或代碼塊。這樣可以確保在同一時(shí)間只有一個(gè)線程能夠執(zhí)行該代碼塊或方法,避免了多線程環(huán)境下的數(shù)據(jù)不一致問(wèn)題,例如:
public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } }
在上面的代碼中,increment()方法是一個(gè)synchronized方法。當(dāng)多個(gè)線程訪問(wèn)這個(gè)方法時(shí),只有一個(gè)線程能夠執(zhí)行該方法的代碼,其他線程將被阻塞。
synchronized關(guān)鍵字也可以用來(lái)修飾代碼塊,如:
public void increment() { synchronized(this) { count++; } }
在上面的代碼中,synchronized關(guān)鍵字修飾的是一個(gè)代碼塊,并且鎖對(duì)象是當(dāng)前對(duì)象(this)
注意:synchronized關(guān)鍵字會(huì)導(dǎo)致線程上下文切換和資源競(jìng)爭(zhēng),所以在使用時(shí)要注意性能問(wèn)題
源碼解析
底層實(shí)現(xiàn)是通過(guò) Java 虛擬機(jī)(JVM)的對(duì)象頭和監(jiān)視器鎖機(jī)制實(shí)現(xiàn)的
具體來(lái)說(shuō),當(dāng)一個(gè)線程訪問(wèn)一個(gè)對(duì)象的 synchronized 方法或代碼塊時(shí),它會(huì)試圖獲取該對(duì)象的監(jiān)視器鎖。如果該鎖未被其他線程占用,該線程將獲得該鎖并執(zhí)行代碼;如果該鎖被其他線程占用,該線程將進(jìn)入阻塞狀態(tài),等待獲取該鎖
synchronized 是Java中用于實(shí)現(xiàn)同步的關(guān)鍵字,它在底層通過(guò)監(jiān)視器鎖(Monitor)來(lái)實(shí)現(xiàn)。下面是synchronized的源碼解析:
在Java中,每個(gè)對(duì)象都有一個(gè)與之關(guān)聯(lián)的監(jiān)視器鎖,也稱(chēng)為內(nèi)置鎖或?qū)ο箧i。當(dāng)線程進(jìn)入一個(gè)synchronized方法或代碼塊時(shí),它會(huì)嘗試獲取該對(duì)象的監(jiān)視器鎖。如果鎖沒(méi)有被其他線程占用,則該線程獲得鎖并開(kāi)始執(zhí)行代碼;如果鎖已經(jīng)被其他線程占用,則該線程將被阻塞,直到鎖被釋放。
在Java虛擬機(jī)中,每個(gè)對(duì)象頭中都包含一部分用于實(shí)現(xiàn)synchronized的相關(guān)信息。這些信息包括:
- mark word:用于存儲(chǔ)對(duì)象的標(biāo)記信息,包括鎖的狀態(tài)。
- Klass pointer:指向?qū)ο蟮念?lèi)元數(shù)據(jù),包括synchronized的相關(guān)信息。
- monitor:與對(duì)象關(guān)聯(lián)的監(jiān)視器,它記錄了當(dāng)前占用鎖的線程、等待鎖的線程隊(duì)列等。
當(dāng)一個(gè)線程嘗試獲取一個(gè)對(duì)象的鎖時(shí),虛擬機(jī)會(huì)檢查對(duì)象頭中的標(biāo)記信息。如果對(duì)象的鎖狀態(tài)為無(wú)鎖狀態(tài),即未被其他線程占用,則該線程可以獲取鎖,并將標(biāo)記信息設(shè)置為鎖定狀態(tài)。如果對(duì)象的鎖狀態(tài)為已鎖定,并且當(dāng)前線程是鎖的所有者,則該線程可以繼續(xù)執(zhí)行代碼。如果對(duì)象的鎖狀態(tài)為已鎖定,并且當(dāng)前線程不是鎖的所有者,則該線程將被放入等待隊(duì)列中,進(jìn)入阻塞狀態(tài)。
當(dāng)持有鎖的線程執(zhí)行完synchronized方法或代碼塊后,它會(huì)釋放鎖,即將對(duì)象頭中的鎖狀態(tài)置為無(wú)鎖狀態(tài),并喚醒等待隊(duì)列中的一個(gè)線程,使其獲取鎖并繼續(xù)執(zhí)行。
需要注意的是,synchronized關(guān)鍵字可以修飾方法和代碼塊。在方法上修飾的synchronized表示對(duì)整個(gè)方法進(jìn)行同步,而在代碼塊上修飾的synchronized表示對(duì)該代碼塊進(jìn)行同步,使用的鎖對(duì)象通常是方法所屬對(duì)象或指定的對(duì)象。
總結(jié)起來(lái),通過(guò)監(jiān)視器鎖的機(jī)制,Java的synchronized能夠保證同一時(shí)刻只有一個(gè)線程訪問(wèn)同步代碼塊或方法,避免了多線程的數(shù)據(jù)競(jìng)爭(zhēng)和并發(fā)問(wèn)題。
這里給出一份簡(jiǎn)化的 synchronized 關(guān)鍵字的源碼:
public void synchronized method() { // 加鎖 Monitor.enter(this); try { // 同步代碼塊 } finally { // 釋放鎖 Monitor.exit(this); } }
在這份代碼中,方法通過(guò)調(diào)用 Monitor.enter 方法獲取當(dāng)前對(duì)象的監(jiān)視器鎖,并在 finally 塊中調(diào)用 Monitor.exit 方法釋放該鎖。因此,在 synchronized 方法內(nèi)部的代碼可以保證在任意時(shí)刻只有一個(gè)線程可以訪問(wèn)
常見(jiàn)面試題
- synchronized 方法和 synchronized 塊的區(qū)別是什么?
作用范圍:synchronized 方法將整個(gè)方法體作為同步區(qū)塊,而 synchronized 塊可以將任意代碼塊作為同步區(qū)塊
鎖的對(duì)象:synchronized 方法鎖定的是整個(gè)對(duì)象,而 synchronized 塊鎖定的是在括號(hào)內(nèi)指定的對(duì)象
可控性:synchronized 方法的同步粒度比較大,不夠靈活;而 synchronized 塊可以更靈活地控制同步代碼塊的大小
綜上所述,在確定同步粒度時(shí),通常使用 synchronized 塊比使用 synchronized 方法更靈活,但是如果整個(gè)方法都需要同步,使用 synchronized 方法會(huì)更加簡(jiǎn)單易懂 - 什么情況下可以使用 synchronized 關(guān)鍵字?
synchronized 關(guān)鍵字可以用于在多線程環(huán)境下保證方法或代碼塊的原子性。具體來(lái)說(shuō),如果一個(gè)線程正在執(zhí)行同步方法或代碼塊,則其他線程將無(wú)法訪問(wèn)該方法或代碼塊
常見(jiàn)情況包括:
當(dāng)多個(gè)線程訪問(wèn)共享資源時(shí),可以使用 synchronized 關(guān)鍵字保證線程的安全
在訪問(wèn)共享變量時(shí),需要對(duì)其進(jìn)行同步控制 - 在線程通信中,可以使用 synchronized 關(guān)鍵字保證線程之間的同步通信
synchronized 關(guān)鍵字的性能開(kāi)銷(xiāo)如何?
synchronized 關(guān)鍵字的使用會(huì)帶來(lái)一些性能開(kāi)銷(xiāo),因?yàn)樗枰诙鄠€(gè)線程之間進(jìn)行同步。當(dāng)線程訪問(wèn)同步代碼塊時(shí),它必須獲得鎖,這會(huì)增加額外的開(kāi)銷(xiāo)。如果同步代碼塊執(zhí)行時(shí)間過(guò)長(zhǎng),其他線程將一直等待,進(jìn)而降低程序的性能。
因此,應(yīng)該盡量避免在高并發(fā)情況下使用 synchronized,或者使用其他的并發(fā)控制機(jī)制,如 java.util.concurrent 包中的鎖和原子操作類(lèi)等。 - synchronized 關(guān)鍵字如何實(shí)現(xiàn)可重入?
“可重入” 指的是同一線程可以多次獲取同一個(gè)鎖。例如,當(dāng)線程 A 進(jìn)入一個(gè)同步塊時(shí),如果它再次試圖進(jìn)入該塊,則可以再次獲取鎖,而不會(huì)發(fā)生死鎖
在 Java 中,synchronized 關(guān)鍵字可以實(shí)現(xiàn)可重入,原因如下:
synchronized 關(guān)鍵字使用對(duì)象監(jiān)視器鎖來(lái)實(shí)現(xiàn)同步。
對(duì)象監(jiān)視器鎖是基于線程的,并且每個(gè)線程有一個(gè)獨(dú)立的計(jì)數(shù)器,用于跟蹤它在當(dāng)前對(duì)象上獲取的鎖的數(shù)量。
當(dāng)線程試圖獲取鎖時(shí),如果它已經(jīng)擁有該鎖,則計(jì)數(shù)器將遞增。
當(dāng)線程退出同步塊時(shí),計(jì)數(shù)器將遞減。
只有當(dāng)計(jì)數(shù)器為零時(shí),該線程才會(huì)釋放鎖。
因此,如果一個(gè)線程在同一對(duì)象上多次進(jìn)入同步塊,它將多次獲得該鎖,并在退出該塊時(shí)多次釋放該鎖。因此,synchronized 關(guān)鍵字是可重入的。
- synchronized 關(guān)鍵字與 lock 機(jī)制的比較?
synchronized 關(guān)鍵字和 Lock 機(jī)制都是用來(lái)保證線程同步的方法。但是它們有一些明顯的差異:
靈活性:Lock 機(jī)制比 synchronized 關(guān)鍵字更靈活,因?yàn)樗峁┝烁嗟逆i定操作,例如可以實(shí)現(xiàn)公平鎖和非公平鎖,還可以實(shí)現(xiàn)讀寫(xiě)鎖。
可中斷性:Lock 機(jī)制可以中斷一個(gè)線程的等待,而 synchronized 關(guān)鍵字不能。
可重入性:synchronized 關(guān)鍵字是自動(dòng)可重入的,而 Lock 機(jī)制必須手動(dòng)實(shí)現(xiàn)。
性能:如果比較的是相同的鎖定操作,synchronized 關(guān)鍵字通常比 Lock 機(jī)制更快,因?yàn)樗莾?nèi)置的。
總體而言,在簡(jiǎn)單的同步情況下,synchronized 關(guān)鍵字更方便,但是在需要更多靈活性的情況下,Lock 機(jī)制可能是一個(gè)更好的選擇。
總結(jié)
到此這篇關(guān)于Java關(guān)鍵字synchronized基本使用詳解的文章就介紹到這了,更多相關(guān)Java關(guān)鍵字synchronized內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入理解java中的synchronized關(guān)鍵字
- 詳解Java中synchronized關(guān)鍵字的死鎖和內(nèi)存占用問(wèn)題
- Java中synchronized關(guān)鍵字修飾方法同步的用法詳解
- java多線程編程之使用Synchronized關(guān)鍵字同步類(lèi)方法
- Java關(guān)鍵字volatile和synchronized作用和區(qū)別
- JAVA面試題 簡(jiǎn)談你對(duì)synchronized關(guān)鍵字的理解
- Java中使用synchronized關(guān)鍵字實(shí)現(xiàn)簡(jiǎn)單同步操作示例
- 舉例講解Java中synchronized關(guān)鍵字的用法
- 實(shí)例解析Java中的synchronized關(guān)鍵字與線程安全問(wèn)題
相關(guān)文章
SpringBoot執(zhí)行異步任務(wù)Async介紹
這篇文章主要為大家介紹了SpringBoot執(zhí)行異步任務(wù)Async示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Springboot實(shí)現(xiàn)從controller中跳轉(zhuǎn)到指定前端頁(yè)面
Springboot實(shí)現(xiàn)從controller中跳轉(zhuǎn)到指定前端頁(yè)面方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10JetBrains?發(fā)布下一代?IDE無(wú)比輕量幾秒就能啟動(dòng)干活
雖然?JetBrains?公司說(shuō)?Fleet?的定位和目標(biāo)并不是代替其他?IDE,但個(gè)人覺(jué)得,?如果?Fleet?火起來(lái)了,其他?IDE?就會(huì)黯然失色,特別是多語(yǔ)言開(kāi)發(fā)者,誰(shuí)愿意裝多個(gè)?IDE?呢?到時(shí)候,可能?JetBrains?以后的所有?IDE?要一統(tǒng)江湖了2021-12-12Java之獲取客戶(hù)端真實(shí)IP地址的實(shí)現(xiàn)
在開(kāi)發(fā)工作中,我們常常需要獲取客戶(hù)端的IP,本文主要介紹了Jav之獲取客戶(hù)端真實(shí)IP地址的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12Java數(shù)據(jù)結(jié)構(gòu)之ArrayList從順序表到實(shí)現(xiàn)
Java中的ArrayList是一種基于數(shù)組實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),支持動(dòng)態(tài)擴(kuò)容和隨機(jī)訪問(wèn)元素,可用于實(shí)現(xiàn)順序表等數(shù)據(jù)結(jié)構(gòu)。ArrayList在內(nèi)存中連續(xù)存儲(chǔ)元素,支持快速的隨機(jī)訪問(wèn)和遍歷。通過(guò)學(xué)習(xí)ArrayList的實(shí)現(xiàn)原理和使用方法,可以更好地掌握J(rèn)ava中的數(shù)據(jù)結(jié)構(gòu)和算法2023-04-04springboot攔截器過(guò)濾token,并返回結(jié)果及異常處理操作
這篇文章主要介紹了springboot攔截器過(guò)濾token,并返回結(jié)果及異常處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09