一篇文章輕松搞懂Java中的自旋鎖
前言
鎖作為并發(fā)共享數(shù)據(jù),保證一致性的工具,在JAVA平臺(tái)有多種實(shí)現(xiàn)(如 synchronized 和 ReentrantLock等等 ) 。這些已經(jīng)寫好提供的鎖為我們開發(fā)提供了便利。
在之前的文章《一文徹底搞懂面試中常問的各種“鎖” 》中介紹了Java中的各種“鎖”,可能對(duì)于不是很了解這些概念的同學(xué)來說會(huì)覺得有點(diǎn)繞,所以我決定拆分出來,逐步詳細(xì)的介紹一下這些鎖的來龍去脈,那么這篇文章就先來會(huì)一會(huì)“自旋鎖”。
正文
出現(xiàn)原因
在我們的程序中,如果存在著大量的互斥同步代碼,當(dāng)出現(xiàn)高并發(fā)的時(shí)候,系統(tǒng)內(nèi)核態(tài)就需要不斷的去掛起線程和恢復(fù)線程,頻繁的此類操作會(huì)對(duì)我們系統(tǒng)的并發(fā)性能有一定影響。同時(shí)聰明的JVM開發(fā)團(tuán)隊(duì)也發(fā)現(xiàn),在程序的執(zhí)行過程中鎖定“共享資源“的時(shí)間片是極短的,如果僅僅是為了這點(diǎn)時(shí)間而去不斷掛起、恢復(fù)線程的話,消耗的時(shí)間可能會(huì)更長,那就“撿了芝麻丟了西瓜”了。
而在一個(gè)多核的機(jī)器中,多個(gè)線程是可以并行執(zhí)行的。如果當(dāng)后面請(qǐng)求鎖的線程沒拿到鎖的時(shí)候,不掛起線程,而是繼續(xù)占用處理器的執(zhí)行時(shí)間,讓當(dāng)前線程執(zhí)行一個(gè)忙循環(huán)(自旋操作),也就是不斷在盯著持有鎖的線程是否已經(jīng)釋放鎖,那么這就是傳說中的自旋鎖了。
自旋鎖開啟
雖然在JDK1.4.2的時(shí)候就引入了自旋鎖,但是需要使用“-XX:+UseSpinning”參數(shù)來開啟。在到了JDK1.6以后,就已經(jīng)是默認(rèn)開啟了。下面我們自己來實(shí)現(xiàn)一個(gè)基于CAS的簡易版自旋鎖。
public class SimpleSpinningLock { /** * 持有鎖的線程,null表示鎖未被線程持有 */ private AtomicReference<Thread> ref = new AtomicReference<>(); public void lock(){ Thread currentThread = Thread.currentThread(); while(!ref.compareAndSet(null, currentThread)){ //當(dāng)ref為null的時(shí)候compareAndSet返回true,反之為false //通過循環(huán)不斷的自旋判斷鎖是否被其他線程持有 } } public void unLock() { Thread cur = Thread.currentThread(); if(ref.get() != cur){ //exception ... } ref.set(null); } }
簡簡單單幾行代碼就實(shí)現(xiàn)了一個(gè)簡陋的自旋鎖,下面我們來測(cè)試一下
public class TestLock { static int count = 0; public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(100); CountDownLatch countDownLatch = new CountDownLatch(100); SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock(); for (int i = 0 ; i < 100 ; i++){ executorService.execute(new Runnable() { @Override public void run() { simpleSpinningLock.lock(); ++count; simpleSpinningLock.unLock(); countDownLatch.countDown(); } }); } countDownLatch.await(); System.out.println(count); } } // 多次執(zhí)行輸出均為:100 ,實(shí)現(xiàn)了鎖的基本功能
通過上面的代碼可以看出,自旋就是在循環(huán)判斷條件是否滿足,那么會(huì)有什么問題嗎?如果鎖被占用很長時(shí)間的話,自旋的線程等待的時(shí)間也會(huì)變長,白白浪費(fèi)掉處理器資源。因此在JDK中,自旋操作默認(rèn)10次,我們可以通過參數(shù)“-XX:PreBlockSpin”來設(shè)置,當(dāng)超過來此參數(shù)的值,則會(huì)使用傳統(tǒng)的線程掛起方式來等待鎖釋放。
自適應(yīng)自旋鎖
隨著JDK的更新,在1.6的時(shí)候,又出現(xiàn)了一個(gè)叫做“自適應(yīng)自旋鎖”的玩意。它的出現(xiàn)使得自旋操作變得聰明起來,不再跟之前一樣死板。所謂的“自適應(yīng)”意味著對(duì)于同一個(gè)鎖對(duì)象,線程的自旋時(shí)間是根據(jù)上一個(gè)持有該鎖的線程的自旋時(shí)間以及狀態(tài)來確定的。例如對(duì)于A鎖對(duì)象來說,如果一個(gè)線程剛剛通過自旋獲得到了鎖,并且該線程也在運(yùn)行中,那么JVM會(huì)認(rèn)為此次自旋操作也是有很大的機(jī)會(huì)可以拿到鎖,因此它會(huì)讓自旋的時(shí)間相對(duì)延長。但是如果對(duì)于B鎖對(duì)象自旋操作很少成功的話,JVM甚至可能直接忽略自旋操作。因此,自適應(yīng)自旋鎖是一個(gè)更加智能,對(duì)我們的業(yè)務(wù)性能更加友好的一個(gè)鎖。
結(jié)語
本來想著在一篇文章里面把“自旋鎖”,“鎖消除”,“鎖粗化”等一些鎖優(yōu)化的概念都介紹完成的,但是發(fā)現(xiàn)可能篇幅會(huì)比較大,對(duì)于沒怎么接觸過這一塊的同學(xué)來說理解起來會(huì)比較吃力,所以決定分開多個(gè)章節(jié)介紹,希望大家都不懂的地方可以多看幾遍,慢慢體會(huì),相信你會(huì)有所收獲的。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
hibernate中HQL如何調(diào)用自定義函數(shù)
這篇文章主要介紹了hibernate中HQL如何調(diào)用自定義函數(shù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java?Timer與TimerTask類使程序計(jì)時(shí)執(zhí)行
這篇文章主要介紹了Java定時(shí)器中的Timer和TimerTask的原理。Timer主要用于Java線程里指定時(shí)間或周期運(yùn)行任務(wù),它是線程安全的,但不提供實(shí)時(shí)性(real-time)保證。接下來就跟隨小編一起深入了解Timer和TimerTask吧2022-02-02SpringMVC實(shí)現(xiàn)Controller的三種方式總結(jié)
這篇文章主要介紹了SpringMVC實(shí)現(xiàn)Controller的三種方式總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法
本篇文章主要介紹了spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01IDEA不識(shí)別Java文件:文件變橙色&顯示后綴名.java的解決
這篇文章主要介紹了IDEA不識(shí)別Java文件:文件變橙色&顯示后綴名.java的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03SpringBoot整合Swagger3生成接口文檔的示例代碼
Swagger 是一個(gè) RESTful API 的開源框架,它的主要目的是幫助開發(fā)者設(shè)計(jì)、構(gòu)建、文檔化和測(cè)試 Web API,本文給大家介紹了SpringBoot整合Swagger3生成接口文檔的流程,并通過代碼講解的非常詳細(xì),需要的朋友可以參考下2024-04-04基于eclipse-temurin鏡像部署spring boot應(yīng)用的實(shí)現(xiàn)示例
本文提供了基于eclipse-temurin鏡像部署Spring Boot應(yīng)用的詳細(xì)實(shí)現(xiàn)示例,通過使用Docker鏡像,可以輕松地創(chuàng)建和管理Spring Boot應(yīng)用程序的容器化環(huán)境,感興趣的可以了解一下2023-08-08SpringBoot實(shí)現(xiàn)接口參數(shù)加密解密的示例代碼
加密解密本身并不是難事,問題是在何時(shí)去處理?SpringMVC?中給我們提供了?ResponseBodyAdvice?和?RequestBodyAdvice,利用這兩個(gè)工具可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理,非常方便。廢話不多說,我們一起來學(xué)習(xí)一下2022-09-09