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

詳解JUC并發(fā)編程之鎖

 更新時(shí)間:2021年12月31日 16:09:01   作者:熟透的蝸牛  
這篇文章主要為大家介紹了JUC并發(fā)編程之鎖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

當(dāng)多個(gè)線程訪問(wèn)一個(gè)對(duì)象時(shí),如果不用考慮這些線程在運(yùn)行環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進(jìn)行額外的同步,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作,調(diào)用這個(gè)對(duì)象的行為都可以獲得正確的結(jié)果,那么這個(gè)對(duì)象就是線程安全的。但是現(xiàn)實(shí)并不是這樣子的,所以JVM實(shí)現(xiàn)了鎖機(jī)制,今天就叭叭叭JAVA中各種各樣的鎖。

1、自旋鎖和自適應(yīng)鎖

自旋鎖:在多線程競(jìng)爭(zhēng)的狀態(tài)下共享數(shù)據(jù)的鎖定狀態(tài)只會(huì)持續(xù)很短的一段時(shí)間,為了這段時(shí)間去掛起和恢復(fù)阻塞線程并不值得,而是讓沒(méi)有獲取到鎖的線程自旋(自旋并不會(huì)放棄CPU的分片時(shí)間)等待當(dāng)前線程釋放鎖,如果自旋超過(guò)了限定的次數(shù)仍然沒(méi)有成功獲取到鎖,就應(yīng)該使用傳統(tǒng)的方式去掛起線程了,在JDK定義中,自旋鎖默認(rèn)的自旋次數(shù)為10次,用戶可以使用參數(shù)-XX:PreBlockSpin來(lái)更改(jdk1.6之后默認(rèn)開(kāi)啟自旋鎖)。

自適應(yīng)鎖:為了解決某些特殊情況,如果自旋剛結(jié)束,線程就釋放了鎖,那么是不是有點(diǎn)不劃算。自適應(yīng)自旋鎖是jdk1.6引入,規(guī)定自旋的時(shí)間不再固定了,而是由前一次在同一個(gè)鎖上的自旋 時(shí)間及鎖的擁有者的狀態(tài)來(lái)決定的。如果在同一個(gè)鎖對(duì)象上,自旋等待剛剛成功獲取過(guò)鎖,并且持有鎖的線程正在運(yùn)行中,那么JVM會(huì)認(rèn)為該線程自旋獲取到鎖的可能性很大,會(huì)自動(dòng)增加等待時(shí)間。反之就認(rèn)為不容易獲取到鎖,而放棄自旋這種方式。

鎖消除:鎖消除時(shí)指虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí),對(duì)一些代碼上要求同步,但是被檢測(cè)到不可能存在共享數(shù)據(jù)競(jìng)爭(zhēng)的鎖進(jìn)行消除。意思就是:在一段代碼中,堆上的所有數(shù)據(jù)都不會(huì)逃逸出去而被其他線程訪問(wèn)到那就可以把他們當(dāng)作棧上的數(shù)據(jù)對(duì)待,認(rèn)為他們是線程私有的,不用再加鎖。

鎖粗化:

  public static void main(String[] args) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("a");
        buffer.append("b");
        buffer.append("c");
        System.out.println("拼接之后的結(jié)果是:>>>>>>>>>>>"+buffer);
    }
  @Override
    @IntrinsicCandidate
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

StringBuffer 在拼接字符串時(shí)是同步的。但是在一系列的操作中都對(duì)同一個(gè)對(duì)象(StringBuffer )反復(fù)加鎖和解鎖,頻繁的進(jìn)行加鎖解鎖操作會(huì)導(dǎo)致不必要的性能損耗,JVM會(huì)將加鎖同步的范圍擴(kuò)展到整個(gè)操作的外部,只加一次鎖。

2、輕量級(jí)鎖和重量級(jí)鎖

這種鎖實(shí)現(xiàn)的背后基于這樣一種假設(shè),即在真實(shí)的情況下我們程序中的大部分同步代碼一般都處于無(wú)鎖競(jìng)爭(zhēng)狀態(tài)(即單線程執(zhí)行環(huán)境),在無(wú)鎖競(jìng)爭(zhēng)的情況下完全可以避免調(diào)用操作系統(tǒng)層面的重量級(jí)互斥鎖, 取而代之的是在monitorenter和monitorexit中只需要依靠一條CAS原子指令就可以完成鎖的獲取及釋放。輕量級(jí)鎖是相對(duì)于重量級(jí)鎖而言的。

輕量級(jí)鎖加鎖過(guò)程

在HotSpot虛擬機(jī)的對(duì)象頭分為兩部分,一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如Hashcode、GC分代年齡、標(biāo)志位等,這部分長(zhǎng)度在32位和64位的虛擬機(jī)中分別是32bit和64bit,稱為Mark Word。另一部分用于存儲(chǔ)指向方法區(qū)對(duì)象類型數(shù)據(jù)的指針,如果是數(shù)組對(duì)象的話,還會(huì)有一個(gè)額外的部分用于存儲(chǔ)數(shù)組長(zhǎng)度。

對(duì)象頭信息是與對(duì)象自身定義的數(shù)據(jù)無(wú)關(guān)的額外存儲(chǔ)成本,Mark Word 被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存儲(chǔ)盡量多的信息。mark word中有兩個(gè)bit存儲(chǔ)鎖標(biāo)記位。

HotSpot虛擬機(jī)對(duì)象頭Mark Word

存儲(chǔ)內(nèi)容標(biāo)志位狀態(tài)
對(duì)象哈希碼,分代年齡01無(wú)鎖
指向鎖記錄的指針00輕量級(jí)鎖
指向重量級(jí)鎖的指針10膨脹重量級(jí)鎖
空,不需要記錄信息11GC標(biāo)記
偏向線程id,偏向時(shí)間戳,對(duì)象分代年齡01可偏向

在代碼進(jìn)入同步代碼塊時(shí),如果此對(duì)象沒(méi)有被鎖定(標(biāo)記位為01狀態(tài)),虛擬機(jī)首先在當(dāng)前線程的棧幀建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前Mark Word的拷貝,然后虛擬機(jī)使用CAS操作嘗試將對(duì)象的Mark Word 更新為指向Lock Record的指針,如果操作成功了,那么這個(gè)線程就有了這個(gè)對(duì)象的鎖,并且將Mark Word 的標(biāo)記位更改為00,表示這個(gè)對(duì)象處于輕量級(jí)鎖定狀態(tài)。如果更新失敗了虛擬機(jī)會(huì)首先檢查是否是當(dāng)前線程擁有了這個(gè)對(duì)象的鎖,如果是就進(jìn)入同步代碼,如果不是,那就說(shuō)明鎖被其他線程占用了。如果有兩個(gè)以上的線程爭(zhēng)奪同一個(gè)鎖,那輕量級(jí)鎖就不再有效,要膨脹為重量級(jí)鎖,鎖標(biāo)記位變?yōu)?0,后面等待的線程就要進(jìn)入阻塞狀態(tài)。

輕量級(jí)鎖解鎖過(guò)程

解鎖過(guò)程同樣使用CAS操作來(lái)進(jìn)行,使用CAS操作將Mark Word 指向Lock Record 指針釋放,如果操作成功,那么整個(gè)同步過(guò)程就完成了,如果釋放失敗,說(shuō)明有其他線程嘗試獲取該鎖,那就在釋放鎖的同時(shí),喚醒被掛起的線程。

3、偏向鎖

JVM 參數(shù) -XX:-UseBiasedLocking 禁用偏向鎖;-XX:+UseBiasedLocking 啟用偏向鎖。

        啟用了偏向鎖才會(huì)執(zhí)行偏向鎖的操作。當(dāng)鎖對(duì)象第一次被線程獲取時(shí),虛擬機(jī)會(huì)把對(duì)象頭中的標(biāo)記位設(shè)置為01,偏向模式。同時(shí)使用CAS操作獲取到當(dāng)前線程的線程ID存儲(chǔ)到Mark Word 中,如果操作成功,那么持有偏向鎖的線程以后每次進(jìn)入這個(gè)鎖相關(guān)的同步塊時(shí),都不需要任何操作,直接進(jìn)入。如果有多個(gè)線程去嘗試獲取這個(gè)鎖時(shí),偏向鎖就宣告無(wú)效,然后會(huì)撤銷偏向或者恢復(fù)到未鎖定。然后再膨脹為重量級(jí)鎖,標(biāo)記位狀態(tài)變?yōu)?0。

4、可重入鎖和不可重入鎖

可重入鎖就是一個(gè)線程獲取到鎖之后,在另一個(gè)代碼塊還需要該鎖,那么不需要重新獲取而可以直接使用該鎖。大多數(shù)的鎖都是可重入鎖。但是CAS自旋鎖不可重入。

package com.xiaojie.juc.thread.lock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @author xiaojie
 * @version 1.0
 * @description: 測(cè)試鎖的重入性
 * @date 2021/12/30 22:09
 */
public class Test01 {
    public synchronized void a() {
        System.out.println(Thread.currentThread().getName() + "運(yùn)行a方法");
        b();
    }
    private synchronized void b() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "運(yùn)行b方法");
    }
    public static void main(String[] args) {
        Test01 test01 = new Test01();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i=0;i<10;i++){
            executorService.execute(() -> test01.a());
        }
    }
}

5、悲觀鎖和樂(lè)觀鎖

悲觀鎖總是悲觀的,總是認(rèn)為會(huì)發(fā)生安全問(wèn)題,所以每次操作都會(huì)加鎖。比如獨(dú)占鎖、傳統(tǒng)數(shù)據(jù)庫(kù)中的行鎖、表鎖、讀鎖、寫(xiě)鎖等。悲觀鎖存在以下幾個(gè)缺點(diǎn):

  • 在多線程競(jìng)爭(zhēng)下,加鎖、釋放鎖會(huì)導(dǎo)致比較多的上下文切換和調(diào)度延遲,引起性能問(wèn)題。
  • 一個(gè)線程占有鎖后,其他線程就得阻塞等待。
  • 如果優(yōu)先級(jí)高的線程等待一個(gè)優(yōu)先級(jí)低的線程,會(huì)導(dǎo)致線程優(yōu)先級(jí)導(dǎo)致,可能引發(fā)性能風(fēng)險(xiǎn)。

樂(lè)觀鎖總是樂(lè)觀的,總是認(rèn)為不會(huì)發(fā)生安全問(wèn)題。在數(shù)據(jù)庫(kù)中可以使用版本號(hào)實(shí)現(xiàn)樂(lè)觀鎖,JAVA中的CAS和一些原子類都是樂(lè)觀鎖的思想。

6、公平鎖和非公平鎖

公平鎖:是指多個(gè)線程在等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序來(lái)依次獲得鎖。

非公平鎖:非公平鎖不需要按照申請(qǐng)鎖的時(shí)間順序來(lái)獲取鎖,而是誰(shuí)能獲取到CPU的時(shí)間片誰(shuí)就先執(zhí)行。非公平鎖的優(yōu)點(diǎn)是吞吐量比公平鎖大,缺點(diǎn)是有可能導(dǎo)致線程優(yōu)先級(jí)反轉(zhuǎn)或者造成過(guò)線程饑餓現(xiàn)象(就是有的線程玩命的一直在執(zhí)行任務(wù),有的線程至死沒(méi)有執(zhí)行一個(gè)任務(wù))。

synchronized中的鎖是非公平鎖,ReentrantLock默認(rèn)也是非公平鎖,但是可以通過(guò)構(gòu)造函數(shù)設(shè)置為公平鎖。

7、共享鎖和獨(dú)占鎖

共享鎖就是同一時(shí)刻允許多個(gè)線程持有的鎖。例如Semaphore(信號(hào)量)、ReentrantReadWriteLock的讀鎖、CountDownLatch倒數(shù)閂等。

獨(dú)占鎖也叫排它鎖、互斥鎖、獨(dú)占鎖是指鎖在同一時(shí)刻只能被一個(gè)線程所持有。例如synchronized內(nèi)置鎖和ReentrantLock顯示鎖,ReentrantReadWriteLock的寫(xiě)鎖都是獨(dú)占鎖。

package com.xiaojie.juc.thread.lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @description: 讀寫(xiě)鎖驗(yàn)證共享鎖和獨(dú)占鎖
 * @author xiaojie
 * @date 2021/12/30 23:28
 * @version 1.0
 */
public class ReadAndWrite {
    static class ReadThred extends Thread {
        private ReentrantReadWriteLock lock;
        private String name;
        public ReadThred(String name, ReentrantReadWriteLock lock) {
            super(name);
            this.lock = lock;
        }
        @Override
        public void run() {
            try {
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + "這是共享鎖。。。。。。");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.readLock().unlock();
                System.out.println(Thread.currentThread().getName() + "釋放鎖成功。。。。。。");
            }
        }
    }
    static class WriteThred extends Thread {
        private ReentrantReadWriteLock lock;
        private String name;
        public WriteThred(String name, ReentrantReadWriteLock lock) {
            super(name);
            this.lock = lock;
        }
        @Override
        public void run() {
            try {
                lock.writeLock().lock();
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + "這是獨(dú)占鎖。。。。。。。。");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.writeLock().unlock();
                System.out.println(Thread.currentThread().getName() + "釋放鎖。。。。。。。");
            }
        }
    }
    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReadThred readThred1 = new ReadThred("read-thread-1", reentrantReadWriteLock);
        ReadThred readThred2 = new ReadThred("read-thread-1", reentrantReadWriteLock);
        WriteThred writeThred1 = new WriteThred("write-thread-1", reentrantReadWriteLock);
        WriteThred writeThred2 = new WriteThred("write-thread-2", reentrantReadWriteLock);
        readThred1.start();
        readThred2.start();
        writeThred1.start();
        writeThred2.start();
    }
}

8、可中斷鎖和不可中斷鎖

可中斷鎖只在搶占鎖的過(guò)程中可以被中斷的鎖如ReentrantLock。

不可中斷鎖是不可中斷的鎖如java內(nèi)置鎖synchronized。

總結(jié):

名稱

優(yōu)點(diǎn)

缺點(diǎn)

使用場(chǎng)景

偏向鎖

加鎖和解鎖不需要CAS操作,沒(méi)有額外的性能消耗,和執(zhí)行非同步方法相比僅存在納秒級(jí)的差距

如果線程間存在鎖競(jìng)爭(zhēng),會(huì)帶來(lái)額外的鎖撤銷的消耗

適用于只有一個(gè)線程訪問(wèn)同步快的場(chǎng)景

輕量級(jí)鎖

競(jìng)爭(zhēng)的線程不會(huì)阻塞,提高了響應(yīng)速度

如線程成始終得不到鎖競(jìng)爭(zhēng)的線程,使用自旋會(huì)消耗CPU性能

追求響應(yīng)時(shí)間,同步快執(zhí)行速度非???/p>

重量級(jí)鎖

線程競(jìng)爭(zhēng)不適用自旋,不會(huì)消耗CPU

線程阻塞,響應(yīng)時(shí)間緩慢,在多線程下,頻繁的獲取釋放鎖,會(huì)帶來(lái)巨大的性能消耗

追求吞吐量,同步快執(zhí)行速度較長(zhǎng)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • spring boot如何實(shí)現(xiàn)切割分片上傳

    spring boot如何實(shí)現(xiàn)切割分片上傳

    這篇文章主要介紹了spring boot如何實(shí)現(xiàn)切割分片上傳,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • RocketMQ NameServer 核心源碼解析

    RocketMQ NameServer 核心源碼解析

    這篇文章主要為大家介紹了RocketMQ NameServer 核心源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 基于Java代碼實(shí)現(xiàn)判斷春節(jié)、端午節(jié)、中秋節(jié)等法定節(jié)假日的方法

    基于Java代碼實(shí)現(xiàn)判斷春節(jié)、端午節(jié)、中秋節(jié)等法定節(jié)假日的方法

    這篇文章主要介紹了基于Java代碼實(shí)現(xiàn)判斷春節(jié)、端午節(jié)、中秋節(jié)等法定節(jié)假日的方法 的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • Java正則表達(dá)式的基本用法和實(shí)例大全

    Java正則表達(dá)式的基本用法和實(shí)例大全

    這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式的基本用法和實(shí)例的相關(guān)資料,大家在使用Java正則表達(dá)式的時(shí)候可查閱這篇文章,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • Spring Boot中的JdbcTemplate是什么及用法小結(jié)

    Spring Boot中的JdbcTemplate是什么及用法小結(jié)

    Spring Boot中的JdbcTemplate是一個(gè)強(qiáng)大的數(shù)據(jù)庫(kù)訪問(wèn)工具,它簡(jiǎn)化了數(shù)據(jù)庫(kù)操作的過(guò)程,在本文中,我們了解了JdbcTemplate的基本概念,并演示了如何在Spring Boot應(yīng)用程序中使用它,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • java使用RabbitMQ實(shí)現(xiàn)延遲消息示例

    java使用RabbitMQ實(shí)現(xiàn)延遲消息示例

    本文介紹了在分布式系統(tǒng)中,使用RabbitMQ實(shí)現(xiàn)延遲消息處理,其中詳細(xì)闡述了RabbitMQ隊(duì)列和交換機(jī)的配置、消息的發(fā)送與接收以及死信隊(duì)列的處理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-10-10
  • 一文詳解SpringBoot中CommandLineRunner接口

    一文詳解SpringBoot中CommandLineRunner接口

    Spring Boot的CommandLineRunner接口是一個(gè)函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動(dòng)后執(zhí)行一些初始化操作,它提供了一個(gè)run方法,該方法在應(yīng)用程序啟動(dòng)后被調(diào)用,本文給大家詳細(xì)介紹了SpringBoot中CommandLineRunner接口,需要的朋友可以參考下
    2023-10-10
  • Mybatis注解方式@Insert的用法

    Mybatis注解方式@Insert的用法

    這篇文章主要介紹了Mybatis注解方式@Insert的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解

    Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Mybatis報(bào)Type interface *.*Mapper is not known to the MapperRegis

    Mybatis報(bào)Type interface *.*Mapper is not&

    本文主要介紹了Mybatis報(bào)Type interface *.*Mapper is not known to the MapperRegis,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07

最新評(píng)論

镇坪县| 兴化市| 建德市| 蒙阴县| 资兴市| 丰镇市| 都江堰市| 渝中区| 邵阳市| 高密市| 千阳县| 海南省| 瑞金市| 成都市| 金昌市| 汝阳县| 灵武市| 巍山| 云霄县| 岑巩县| 手游| 曲阳县| 呼和浩特市| 仙桃市| 蒙自县| 泸州市| 祁连县| 五寨县| 塘沽区| 临猗县| 鱼台县| 胶南市| 东光县| 门头沟区| 武乡县| 桦南县| 张家川| 曲靖市| 崇阳县| 穆棱市| 林芝县|