欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的自旋鎖解析

 更新時(shí)間:2023年10月13日 09:21:31   作者:李長淵哦  
這篇文章主要介紹了Java中的自旋鎖解析,自旋鎖是指當(dāng)一個(gè)線程嘗試獲取某個(gè)鎖時(shí),如果該鎖已被其他線程占用,就一直循環(huán)檢測鎖是否被釋放,而不是進(jìn)入線程掛起或睡眠狀態(tài),需要的朋友可以參考下

一、自旋鎖介紹

什么是自旋鎖

自旋鎖是指當(dāng)一個(gè)線程嘗試獲取某個(gè)鎖時(shí),如果該鎖已被其他線程占用,就一直循環(huán)檢測鎖是否被釋放,而不是進(jìn)入線程掛起或睡眠狀態(tài)。

為什么要使用自旋鎖

多個(gè)線程對同一個(gè)變量一直使用CAS操作,那么會(huì)有大量修改操作,從而產(chǎn)生大量的緩存一致性流量,因?yàn)槊恳淮蜟AS操作都會(huì)發(fā)出廣播通知其他處理器,從而影響程序的性能。

線程自旋與線程阻塞

阻塞的缺點(diǎn)顯而易見,線程一旦進(jìn)入阻塞(Block),再被喚醒的代價(jià)比較高,性能較差。自旋的優(yōu)點(diǎn)是線程還是Runnable的,只是在執(zhí)行空代碼。當(dāng)然一直自旋也會(huì)白白消耗計(jì)算資源,所以常見的做法是先自旋一段時(shí)間,還沒拿到鎖就進(jìn)入阻塞。JVM在處理synchrized實(shí)現(xiàn)時(shí)就是采用了這種折中的方案,并提供了調(diào)節(jié)自旋的參數(shù)。

首先來對比一下互斥鎖和自旋鎖。

  • 互斥鎖:從等待到解鎖過程,線程會(huì)從block狀態(tài)變?yōu)閞unning狀態(tài),過程中有線程上下文的切換,搶占CPU等開銷。
  • 自旋鎖:從等待到解鎖過程,線程一直處于running狀態(tài),沒有上下文的切換。

雖然自旋鎖效率比互斥鎖高,但它會(huì)存在下面兩個(gè)問題

  1. 自旋鎖一直占用CPU,在未獲得鎖的情況下,一直運(yùn)行,如果不能在很短的時(shí)間內(nèi)獲得鎖,會(huì)導(dǎo)致CPU效率降低。
  2. 試圖遞歸地獲得自旋鎖會(huì)引起死鎖。遞歸程序決不能在持有自旋鎖時(shí)調(diào)用它自己,也決不能在遞歸調(diào)用時(shí)試圖獲得相同的自旋鎖。

由此可見,我們要慎重的使用自旋鎖,自旋鎖適合于鎖使用者保持鎖時(shí)間比較短并且鎖競爭不激烈的情況。正是由于自旋鎖使用者一般保持鎖時(shí)間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠(yuǎn)高于互斥鎖。

二、代碼舉例

/**
 * @author lichangyuan
 * @create 2021-10-08 10:50
 */
public class SpinLock {
    public static void main(String[] args) throws InterruptedException {
        //設(shè)置100容量線程池
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        //計(jì)數(shù)器用于阻塞
        CountDownLatch countDownLatch = new CountDownLatch(10);
        //創(chuàng)建自旋鎖對象
        SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    simpleSpinningLock.lock();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("子線程:" + Thread.currentThread().getName() + "執(zhí)行");
                    simpleSpinningLock.unLock();
                    //確認(rèn)已經(jīng)連接完畢后再進(jìn)行操作,將count值減1
                    countDownLatch.countDown();
                }
            });
        }
        //調(diào)用await()方法的線程會(huì)被掛起,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行,沒有則調(diào)用countDown則繼續(xù)阻塞
        countDownLatch.await();
    }
}
class SimpleSpinningLock {
    /**
     * 持有鎖的線程,null表示鎖未被線程持有
     */
    private AtomicReference<Thread> sign = new AtomicReference<>();
    /**
     * 調(diào)用lock方法時(shí),如果sign當(dāng)前值為null,說明自旋鎖還沒有被占用,將sign設(shè)置為currentThread,并進(jìn)行鎖定。
     * 調(diào)用lock方法時(shí),如果sign當(dāng)前值不為null,說明自旋鎖已經(jīng)被其他線程占用,當(dāng)前線程就會(huì)在while中繼續(xù)循環(huán)檢測。
     */
    public void lock() {
        //返回的正是執(zhí)行當(dāng)前代碼指令的線程引用
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子對象應(yīng)為的值。
        //val:如果原子整數(shù)等于期望值,則該值指定要更新的值。
        while (!sign.compareAndSet(null, currentThread)) {
            //當(dāng)ref為null的時(shí)候compareAndSet返回true,反之為false
            //通過循環(huán)不斷的自旋判斷鎖是否被其他線程持有
        }
    }
    /**
     * 調(diào)用unlock方法時(shí),會(huì)將sign置為空,相當(dāng)于釋放自旋鎖。
     */
    public void unLock() {
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子對象應(yīng)為的值。
        //val:如果原子整數(shù)等于期望值,則該值指定要更新的值。
        sign.compareAndSet(currentThread, null);
    }
}

總結(jié)

由于自旋鎖只是在當(dāng)前線程不停地執(zhí)行循環(huán)體,不進(jìn)行線程狀態(tài)的切換,因此響應(yīng)速度更快。

但當(dāng)線程數(shù)不停增加時(shí),性能下降明顯,因?yàn)槊總€(gè)線程都需要占用CPU時(shí)間。

如果線程競爭不激烈,并且保持鎖的時(shí)間很短,則適合使用自旋鎖。

到此這篇關(guān)于Java中的自旋鎖解析的文章就介紹到這了,更多相關(guān)Java自旋鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC接收復(fù)雜集合對象(參數(shù))代碼示例

    SpringMVC接收復(fù)雜集合對象(參數(shù))代碼示例

    這篇文章主要介紹了SpringMVC接收復(fù)雜集合對象(參數(shù))代碼示例,舉接收List<String>、List<User>、List<Map<String,Object>>、User[]、User(bean里面包含List)幾種較為復(fù)雜的集合參數(shù),具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • java實(shí)現(xiàn)PPT轉(zhuǎn)PDF出現(xiàn)中文亂碼問題的解決方法

    java實(shí)現(xiàn)PPT轉(zhuǎn)PDF出現(xiàn)中文亂碼問題的解決方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)PPT轉(zhuǎn)PDF出現(xiàn)中文亂碼問題的解決方法,進(jìn)行了詳細(xì)的問題分析,需要的朋友可以參考下
    2015-11-11
  • mybatis plus的3種查詢方式(小結(jié))

    mybatis plus的3種查詢方式(小結(jié))

    這篇文章主要介紹了mybatis plus的3種查詢方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • jedis的return行為源碼解析

    jedis的return行為源碼解析

    這篇文章主要為大家介紹了jedis的return行為源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Springboot 如何關(guān)閉自動(dòng)配置

    Springboot 如何關(guān)閉自動(dòng)配置

    這篇文章主要介紹了Springboot 如何關(guān)閉自動(dòng)配置的操作,具有很好的開車價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java數(shù)據(jù)類型的規(guī)則

    Java數(shù)據(jù)類型的規(guī)則

    這篇文章主要介紹了Java數(shù)據(jù)類型的規(guī)則的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-12-12
  • 一文讓你搞懂如何手寫一個(gè)redis分布式鎖

    一文讓你搞懂如何手寫一個(gè)redis分布式鎖

    既然要搞懂Redis分布式鎖,那肯定要有一個(gè)需要它的場景。高并發(fā)售票問題就是一個(gè)經(jīng)典案例。本文就來利用這個(gè)場景手寫一個(gè)redis分布式鎖,讓你徹底搞懂它
    2022-11-11
  • SpringBoot攔截器實(shí)現(xiàn)登錄攔截的示例代碼

    SpringBoot攔截器實(shí)現(xiàn)登錄攔截的示例代碼

    本文主要介紹了SpringBoot攔截器實(shí)現(xiàn)登錄攔截,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • IDEA配置靜態(tài)資源熱加載操作(Springboot修改靜態(tài)資源不重啟)

    IDEA配置靜態(tài)資源熱加載操作(Springboot修改靜態(tài)資源不重啟)

    這篇文章主要介紹了IDEA配置靜態(tài)資源熱加載操作(Springboot修改靜態(tài)資源不重啟),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • mybatis使用xml進(jìn)行增刪改查代碼解析

    mybatis使用xml進(jìn)行增刪改查代碼解析

    這篇文章主要介紹了mybatis使用xml進(jìn)行增刪改查代碼解析,分享了相關(guān)配置和代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02

最新評論