最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java自旋鎖及自旋的好處詳解

 更新時(shí)間:2023年10月13日 10:16:20   作者:a904364908  
這篇文章主要介紹了Java自旋鎖及自旋的好處詳解,自旋就是自己在這里不停地循環(huán),直到目標(biāo)達(dá)成,而不像普通的鎖那樣,如果獲取不到鎖就進(jìn)入阻塞,需要的朋友可以參考下

什么是自旋

我們了解什么叫自旋?“自旋”可以理解為“自我旋轉(zhuǎn)”,這里的“旋轉(zhuǎn)”指“循環(huán)”,比如 while 循環(huán)或者 for 循環(huán)。“自旋”就是自己在這里不停地循環(huán),直到目標(biāo)達(dá)成。而不像普通的鎖那樣,如果獲取不到鎖就進(jìn)入阻塞。

對(duì)比自旋和非自旋的獲取鎖的流程

我們用這樣一張流程圖來(lái)對(duì)比一下自旋鎖和非自旋鎖的獲取鎖的過(guò)程。

自旋和非自旋對(duì)比

我們來(lái)看自旋鎖,它并不會(huì)放棄 CPU 時(shí)間片,而是通過(guò)自旋等待鎖的釋放,也就是說(shuō),它會(huì)不停地再次地嘗試獲取鎖,如果失敗就再次嘗試,直到成功為止。

我們?cè)賮?lái)看下非自旋鎖,非自旋鎖和自旋鎖是完全不一樣的,如果它發(fā)現(xiàn)此時(shí)獲取不到鎖,它就把自己的線程切換狀態(tài),讓線程休眠,然后 CPU 就可以在這段時(shí)間去做很多其他的事情,直到之前持有這把鎖的線程釋放了鎖,于是 CPU 再把之前的線程恢復(fù)回來(lái),讓這個(gè)線程再去嘗試獲取這把鎖。如果再次失敗,就再次讓線程休眠,如果成功,一樣可以成功獲取到同步資源的鎖。

可以看出,非自旋鎖和自旋鎖最大的區(qū)別,就是如果它遇到拿不到鎖的情況,它會(huì)把線程阻塞,直到被喚醒。而自旋鎖會(huì)不停地嘗試。那么,自旋鎖這樣不停嘗試的好處是什么呢?

自旋鎖的好處

阻塞和喚醒線程都是需要高昂的開銷的,如果同步代碼塊中的內(nèi)容不復(fù)雜,那么可能轉(zhuǎn)換線程帶來(lái)的開銷比實(shí)際業(yè)務(wù)代碼執(zhí)行的開銷還要大。

在很多場(chǎng)景下,可能我們的同步代碼塊的內(nèi)容并不多,所以需要的執(zhí)行時(shí)間也很短,如果我們僅僅為了這點(diǎn)時(shí)間就去切換線程狀態(tài),那么其實(shí)不如讓線程不切換狀態(tài),而是讓它自旋地嘗試獲取鎖,等待其他線程釋放鎖,有時(shí)我只需要稍等一下,就可以避免上下文切換等開銷,提高了效率。

用一句話總結(jié)自旋鎖的好處,那就是自旋鎖用循環(huán)去不停地嘗試獲取鎖,讓線程始終處于 Runnable 狀態(tài),節(jié)省了線程狀態(tài)切換帶來(lái)的開銷。

AtomicLong 的實(shí)現(xiàn)

在 Java 1.5 版本及以上的并發(fā)包中,也就是 java.util.concurrent 的包中,里面的原子類基本都是自旋鎖的實(shí)現(xiàn)。

比如我們看一個(gè) AtomicLong 的實(shí)現(xiàn),里面有一個(gè) getAndIncrement 方法,源碼如下:

public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

可以看到它調(diào)用了一個(gè) unsafe.getAndAddLong,所以我們?cè)賮?lái)看這個(gè)方法:

public final long getAndAddLong (Object var1,long var2, long var4){
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while (!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
    return var6;
}

在這個(gè)方法中,它用了一個(gè) do while 循環(huán)。這里就很明顯了:

do {
    var6 = this.getLongVolatile(var1, var2);
} 
while (!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

這里的 do-while 循環(huán)就是一個(gè)自旋操作,如果在修改過(guò)程中遇到了其他線程競(jìng)爭(zhēng)導(dǎo)致沒(méi)修改成功的情況,就會(huì) while 循環(huán)里進(jìn)行死循環(huán),直到修改成功為止。

自己實(shí)現(xiàn)一個(gè)可重入的自旋鎖

下面我們來(lái)看一個(gè)自己實(shí)現(xiàn)可重入的自旋鎖。

代碼如下所示:

package lesson27;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
/**
 * 描述:     實(shí)現(xiàn)一個(gè)可重入的自旋鎖
 */
public class ReentrantSpinLock  {
    private AtomicReference<Thread> owner = new AtomicReference<>();
    //重入次數(shù)
    private int count = 0;
    public void lock() {
        Thread t = Thread.currentThread();
        if (t == owner.get()) {
            ++count;
            return;
        }
        //自旋獲取鎖
        while (!owner.compareAndSet(null, t)) {
            System.out.println("自旋了");
        }
    }
    public void unlock() {
        Thread t = Thread.currentThread();
        //只有持有鎖的線程才能解鎖
        if (t == owner.get()) {
            if (count > 0) {
                --count;
            } else {
                //此處無(wú)需CAS操作,因?yàn)闆](méi)有競(jìng)爭(zhēng),因?yàn)橹挥芯€程持有者才能解鎖
                owner.set(null);
            }
        }
    }
    public static void main(String[] args) {
        ReentrantSpinLock spinLock = new ReentrantSpinLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "開始嘗試獲取自旋鎖");
                spinLock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "獲取到了自旋鎖");
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    spinLock.unlock();
                    System.out.println(Thread.currentThread().getName() + "釋放了了自旋鎖");
                }
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
    }
}

這段代碼的運(yùn)行結(jié)果是:

...
自旋了
自旋了
自旋了
自旋了
自旋了
自旋了
自旋了
自旋了
Thread-0釋放了了自旋鎖
Thread-1獲取到了自旋鎖

前面會(huì)打印出很多“自旋了”,說(shuō)明自旋期間,CPU依然在不停運(yùn)轉(zhuǎn)。

缺點(diǎn)

那么自旋鎖有沒(méi)有缺點(diǎn)呢?其實(shí)自旋鎖是有缺點(diǎn)的。它最大的缺點(diǎn)就在于雖然避免了線程切換的開銷,但是它在避免線程切換開銷的同時(shí)也帶來(lái)了新的開銷,因?yàn)樗枰煌5萌L試獲取鎖。如果這把鎖一直不能被釋放,那么這種嘗試只是無(wú)用的嘗試,會(huì)白白浪費(fèi)處理器資源。也就是說(shuō),雖然一開始自旋鎖的開銷低于線程切換,但是隨著時(shí)間的增加,這種開銷也是水漲船高,后期甚至?xí)^(guò)線程切換的開銷,得不償失。

適用場(chǎng)景

所以我們就要看一下自旋鎖的適用場(chǎng)景。首先,自旋鎖適用于并發(fā)度不是特別高的場(chǎng)景,以及臨界區(qū)比較短小的情況,這樣我們可以利用避免線程切換來(lái)提高效率。

可是如果臨界區(qū)很大,線程一旦拿到鎖,很久才會(huì)釋放的話,那就不合適用自旋鎖,因?yàn)樽孕龝?huì)一直占用 CPU 卻無(wú)法拿到鎖,白白消耗資源。

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

相關(guān)文章

  • 我勸你謹(jǐn)慎使用Spring中的@Scheduled注解

    我勸你謹(jǐn)慎使用Spring中的@Scheduled注解

    這篇文章主要介紹了Spring中的@Scheduled注解使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • SpringBoot實(shí)現(xiàn)異步調(diào)用的全過(guò)程

    SpringBoot實(shí)現(xiàn)異步調(diào)用的全過(guò)程

    文章介紹了在Java的SpringBoot中實(shí)現(xiàn)異步請(qǐng)求和異步調(diào)用的方法,包括使用`@Async`注解、Java原生線程池和Spring的`TaskExecutor`,文章還討論了何時(shí)使用異步請(qǐng)求和不同異步處理方式的優(yōu)缺點(diǎn)
    2025-12-12
  • 詳解RabbitMQ延遲隊(duì)列的基本使用和優(yōu)化

    詳解RabbitMQ延遲隊(duì)列的基本使用和優(yōu)化

    這篇文章主要介紹了詳解RabbitMQ延遲隊(duì)列的基本使用和優(yōu)化,延遲隊(duì)列中的元素都是帶有時(shí)間屬性的。延遲隊(duì)列就是用來(lái)存放需要在指定時(shí)間被處理的元素的隊(duì)列,需要的朋友可以參考下
    2023-05-05
  • 基于SpringBoot實(shí)現(xiàn)HTTP請(qǐng)求簽名驗(yàn)證機(jī)制

    基于SpringBoot實(shí)現(xiàn)HTTP請(qǐng)求簽名驗(yàn)證機(jī)制

    在分布式系統(tǒng)交互中,API接口的安全性至關(guān)重要,本文將深入解析基于Spring Boot實(shí)現(xiàn)的HTTP請(qǐng)求簽名驗(yàn)證機(jī)制,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • Spring?Boot中YAML?配置文件常見問(wèn)題及解決方案

    Spring?Boot中YAML?配置文件常見問(wèn)題及解決方案

    在Spring Boot項(xiàng)目中,配置文件是不可或缺的一部分,用于自定義應(yīng)用行為、覆蓋默認(rèn)設(shè)置,本文給大家介紹SpringBoot中YAML配置文件的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • 通過(guò)ibatis解決sql注入問(wèn)題

    通過(guò)ibatis解決sql注入問(wèn)題

    這篇文章主要介紹了通過(guò)ibatis解決sql注入問(wèn)題,需要的朋友可以參考下
    2017-09-09
  • Arthas排查Kubernetes中應(yīng)用頻繁掛掉重啟異常

    Arthas排查Kubernetes中應(yīng)用頻繁掛掉重啟異常

    這篇文章主要為大家介紹了Arthas排查Kubernetes中應(yīng)用頻繁掛掉重啟的異常分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步
    2022-02-02
  • 詳解Java日志正確使用姿勢(shì)

    詳解Java日志正確使用姿勢(shì)

    這篇文章主要介紹了Java日志正確使用姿勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Spring觀察者模式之事件發(fā)布訂閱實(shí)現(xiàn)和源碼詳解

    Spring觀察者模式之事件發(fā)布訂閱實(shí)現(xiàn)和源碼詳解

    這篇文章主要介紹了Spring觀察者模式之事件發(fā)布訂閱實(shí)現(xiàn)和源碼詳解,Spring認(rèn)為發(fā)布訂閱主題,其實(shí)可以理解為事件驅(qū)動(dòng)的編碼,先來(lái)實(shí)現(xiàn)以下Spring容器中的事件發(fā)布訂閱,需要的朋友可以參考下
    2024-01-01
  • Spring源碼解析容器初始化構(gòu)造方法

    Spring源碼解析容器初始化構(gòu)造方法

    這篇文章主要介紹了Spring源碼解析容器初始化構(gòu)造方法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07

最新評(píng)論

商丘市| 永川市| 杭锦后旗| 磴口县| 岳西县| 临汾市| 大名县| 拜城县| 全椒县| 秦皇岛市| 华阴市| 桓台县| 深州市| 来宾市| 股票| 全南县| 尉犁县| 乌兰察布市| 孝义市| 抚松县| 彰武县| 广元市| 阜城县| 四子王旗| 普格县| 富民县| 新疆| 嘉义县| 天津市| 阿图什市| 宁河县| 库伦旗| 同仁县| 新龙县| 岳阳县| 肥城市| 济南市| 时尚| 葵青区| 东安县| 银川市|