Java 中 synchronized 的使用方式和鎖升級(jí)
在 Java 并發(fā)編程中,synchronized
是一個(gè)非常重要的關(guān)鍵字,用于實(shí)現(xiàn)線程同步,保證在同一時(shí)刻只有一個(gè)線程可以訪問(wèn)被同步的代碼塊或方法,從而避免多線程帶來(lái)的數(shù)據(jù)不一致等問(wèn)題。同時(shí),Java 虛擬機(jī)(JVM)為了提高ynchronized
的性能,引入了鎖升級(jí)機(jī)制。下面我們就來(lái)詳細(xì)介紹ynchronized
的使用和鎖升級(jí)過(guò)程。
一、synchronized 的使用方式
(一)修飾普通方法
當(dāng)synchronized
修飾一個(gè)普通方法時(shí),鎖對(duì)象是當(dāng)前對(duì)象(this
)。也就是說(shuō),當(dāng)一個(gè)線程進(jìn)入該方法時(shí),會(huì)自動(dòng)獲取當(dāng)前對(duì)象的鎖,其他線程想要進(jìn)入該方法,必須等待當(dāng)前線程釋放鎖。例如:
public class SynchronizedExample { public synchronized void synchronizedMethod() { // 線程同步的代碼邏輯 System.out.println("線程 " + Thread.currentThread().getName() + " 進(jìn)入同步方法"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程 " + Thread.currentThread().getName() + " 退出同步方法"); } }
在上述代碼中,synchronizedMethod
是一個(gè)同步方法,多個(gè)線程同時(shí)調(diào)用該方法時(shí),會(huì)依次排隊(duì)執(zhí)行,保證了方法內(nèi)代碼的線程安全性。
(二)修飾靜態(tài)方法
當(dāng)synchronized
修飾靜態(tài)方法時(shí),鎖對(duì)象是該類的Class
對(duì)象。因?yàn)殪o態(tài)方法屬于類級(jí)別,不依賴于具體的對(duì)象實(shí)例,所以使用類的Class
對(duì)象作為鎖。示例如下:
public class StaticSynchronizedExample { public static synchronized void staticSynchronizedMethod() { System.out.println("線程 " + Thread.currentThread().getName() + " 進(jìn)入靜態(tài)同步方法"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程 " + Thread.currentThread().getName() + " 退出靜態(tài)同步方法"); } }
多個(gè)線程調(diào)用staticSynchronizedMethod
時(shí),會(huì)對(duì)類的Class
對(duì)象進(jìn)行加鎖,從而實(shí)現(xiàn)線程同步。
(三)修飾代碼塊
synchronized
還可以修飾代碼塊,此時(shí)需要顯式指定鎖對(duì)象。鎖對(duì)象可以是任意對(duì)象,只要保證在需要同步的代碼塊中使用相同的鎖對(duì)象即可。比如:
public class BlockSynchronizedExample { private Object lock = new Object(); public void blockSynchronized() { synchronized (lock) { System.out.println("線程 " + Thread.currentThread().getName() + " 進(jìn)入同步代碼塊"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程 " + Thread.currentThread().getName() + " 退出同步代碼塊"); } } }
在這個(gè)例子中,lock
對(duì)象作為同步代碼塊的鎖,多個(gè)線程訪問(wèn)blockSynchronized
方法時(shí),會(huì)競(jìng)爭(zhēng)lock
對(duì)象的鎖。
二、synchronized 的鎖升級(jí)
為了提高synchronized
的性能,JVM 引入了鎖升級(jí)機(jī)制,從無(wú)鎖狀態(tài)開(kāi)始,根據(jù)競(jìng)爭(zhēng)情況逐步升級(jí)為偏向鎖、輕量級(jí)鎖和重量級(jí)鎖。
(一)無(wú)鎖
無(wú)鎖狀態(tài)是最基本的狀態(tài),當(dāng)沒(méi)有線程競(jìng)爭(zhēng)鎖時(shí),對(duì)象處于無(wú)鎖狀態(tài)。此時(shí),線程可以直接訪問(wèn)被synchronized
修飾的代碼,無(wú)需進(jìn)行任何加鎖操作,因此性能最高。
(二)偏向鎖
當(dāng)一個(gè)線程首次訪問(wèn)被synchronized
修飾的代碼時(shí),JVM 會(huì)將對(duì)象頭中的鎖標(biāo)志位設(shè)置為偏向鎖模式,并將該線程的 ID 記錄在對(duì)象頭中。此后,當(dāng)該線程再次訪問(wèn)同一對(duì)象的同步代碼時(shí),無(wú)需進(jìn)行額外的加鎖操作,直接進(jìn)入同步代碼塊,因?yàn)?JVM 認(rèn)為該線程很可能會(huì)再次訪問(wèn)。偏向鎖的存在減少了無(wú)競(jìng)爭(zhēng)情況下的鎖開(kāi)銷。
(三)輕量級(jí)鎖
當(dāng)有第二個(gè)線程試圖訪問(wèn)同一個(gè)對(duì)象的同步代碼時(shí),偏向鎖會(huì)升級(jí)為輕量級(jí)鎖。JVM 會(huì)在當(dāng)前線程的棧幀中創(chuàng)建一個(gè)鎖記錄(Lock Record),并將對(duì)象頭中的 Mark Word 復(fù)制到鎖記錄中,然后嘗試使用 CAS(Compare - And - Swap,比較并交換)操作將對(duì)象頭的 Mark Word 替換為指向鎖記錄的指針。如果 CAS 操作成功,當(dāng)前線程就獲得了輕量級(jí)鎖,可以進(jìn)入同步代碼塊;如果失敗,說(shuō)明存在競(jìng)爭(zhēng),輕量級(jí)鎖會(huì)升級(jí)為重量級(jí)鎖。
(四)重量級(jí)鎖
當(dāng)輕量級(jí)鎖競(jìng)爭(zhēng)失敗,即多個(gè)線程同時(shí)競(jìng)爭(zhēng)鎖時(shí),會(huì)升級(jí)為重量級(jí)鎖。此時(shí),JVM 會(huì)使用操作系統(tǒng)的互斥量(Mutex)來(lái)實(shí)現(xiàn)鎖機(jī)制,線程會(huì)進(jìn)入阻塞狀態(tài),等待鎖的釋放。重量級(jí)鎖的性能相對(duì)較低,因?yàn)榫€程的阻塞和喚醒需要操作系統(tǒng)的干預(yù),會(huì)帶來(lái)較大的開(kāi)銷。
鎖升級(jí)的過(guò)程是 JVM 根據(jù)實(shí)際的線程競(jìng)爭(zhēng)情況動(dòng)態(tài)調(diào)整的,目的是在保證線程安全性的同時(shí),盡可能提高synchronized
的性能。
總之,synchronized
是 Java 并發(fā)編程中實(shí)現(xiàn)線程同步的重要手段,理解其使用方式和鎖升級(jí)機(jī)制,對(duì)于編寫(xiě)高效、安全的多線程程序具有重要意義。在實(shí)際開(kāi)發(fā)中,我們應(yīng)該根據(jù)具體的場(chǎng)景合理使用synchronized
,并注意鎖的粒度和性能優(yōu)化,以充分發(fā)揮多線程的優(yōu)勢(shì)。
到此這篇關(guān)于Java 中 synchronized 的使用和鎖升級(jí)的文章就介紹到這了,更多相關(guān)Java synchronized 使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java中的synchronized有幾種加鎖方式(實(shí)例詳解)
- Java中復(fù)雜的Synchronized關(guān)鍵字使用方法詳解
- Java中synchronized的四種用法詳解
- Java關(guān)鍵字synchronized基本使用詳解
- Java并發(fā)編程中的synchronized關(guān)鍵字詳細(xì)解讀
- Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索
- 深入了解Java中Synchronized關(guān)鍵字的實(shí)現(xiàn)原理
- 一文帶你搞懂Java中Synchronized和Lock的原理與使用
- Java同步鎖synchronized用法的最全總結(jié)
相關(guān)文章
springboot登陸頁(yè)面圖片驗(yàn)證碼簡(jiǎn)單的web項(xiàng)目實(shí)現(xiàn)
這篇文章主要介紹了springboot登陸頁(yè)面圖片驗(yàn)證碼簡(jiǎn)單的web項(xiàng)目實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04linux用java -jar啟動(dòng)jar包緩慢的問(wèn)題
這篇文章主要介紹了linux用java -jar啟動(dòng)jar包緩慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-09-09Springboot與vue實(shí)例講解實(shí)現(xiàn)前后端分離的人事管理系統(tǒng)
這篇文章主要介紹了如何用Java實(shí)現(xiàn)企業(yè)人事管理系統(tǒng),文中采用springboot+vue實(shí)現(xiàn)前后端分離,感興趣的小伙伴可以學(xué)習(xí)一下2022-06-06java CompletableFuture實(shí)現(xiàn)異步編排詳解
這篇文章主要為大家介紹了java CompletableFuture實(shí)現(xiàn)異步編排詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01SpringBoot中定時(shí)任務(wù)的使用方法解析
這篇文章主要介紹了SpringBoot中定時(shí)任務(wù)的使用方法解析,@EnableScheduling?注解,它的作用是發(fā)現(xiàn)注解?@Scheduled的任務(wù)并由后臺(tái)執(zhí)行,沒(méi)有它的話將無(wú)法執(zhí)行定時(shí)任務(wù),需要的朋友可以參考下2024-01-01RocketMQ中消費(fèi)者的消費(fèi)進(jìn)度管理
這篇文章主要介紹了RocketMQ中消費(fèi)者的消費(fèi)進(jìn)度管理,業(yè)務(wù)實(shí)現(xiàn)消費(fèi)回調(diào)的時(shí)候,當(dāng)且僅當(dāng)此回調(diào)函數(shù)返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS ,RocketMQ才會(huì)認(rèn)為這批消息(默認(rèn)是1條)是消費(fèi)完成的,需要的朋友可以參考下2023-10-10SpringBoot實(shí)現(xiàn)發(fā)送郵件功能過(guò)程圖解
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)發(fā)送郵件功能過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03往DAO類中注入@PersistenceContext和@Resource的區(qū)別詳解
這篇文章主要介紹了往DAO類中注入@PersistenceContext和@Resource的區(qū)別詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02