国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized

這篇具有很好參考價(jià)值的文章主要介紹了【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1. 前言

1.1 并發(fā)編程中存在線程安全問(wèn)題

  1. 存在共享數(shù)據(jù);

  2. 多線程共同操作共享數(shù)。

關(guān)鍵字 synchronized 可以保證在同一時(shí)刻,只有一個(gè)線程可以執(zhí)行某個(gè)方法或某個(gè)代碼塊,同時(shí) synchronized 可以保證一個(gè)線程的變化可見(jiàn)(可見(jiàn)性),即可以代替 volatile。

1.2 設(shè)計(jì)同步器的意義

多線程編程中,有可能會(huì)出現(xiàn)多個(gè)線程同時(shí)訪問(wèn)同一個(gè)共享、可變資源的情況,這個(gè)資源我們稱(chēng)之為臨界資源。這種資源可能是:對(duì)象、變量、文件等。

  • 共享:資源可以由多個(gè)線程同時(shí)訪問(wèn);

  • 可變:資源可以在其生命周期內(nèi)被修改。

引出的問(wèn)題:由于線程執(zhí)行的過(guò)程是不可控的,所以需要采用同步機(jī)制來(lái)協(xié)調(diào)對(duì)對(duì)象可變狀態(tài)的訪問(wèn)。

1.3 如何解決線程并發(fā)安全問(wèn)題?

實(shí)際上,所有的并發(fā)模式在解決線程安全問(wèn)題時(shí),采用的方案都是序列化訪問(wèn)臨界資源。即在同一時(shí)刻,只能有一個(gè)線程訪問(wèn)臨界資源,也稱(chēng)作同步互斥訪問(wèn)。

Java 中,提供了兩種方式來(lái)實(shí)現(xiàn)同步互斥訪問(wèn):synchronizedLock。

同步器的本質(zhì)就是加鎖。加鎖目的:序列化訪問(wèn)臨界資源,即同一時(shí)刻只能有一個(gè)線程訪問(wèn)臨界資源(同步互斥訪問(wèn))。

不過(guò)有一點(diǎn)需要區(qū)別的是:當(dāng)多個(gè)線程執(zhí)行一個(gè)方法時(shí),該方法內(nèi)部的局部變量并不是臨界資源,因?yàn)檫@些局部變量是在每個(gè)線程的私有棧中,并不具有共享性,不會(huì)導(dǎo)致線程安全問(wèn)題。

2. synchronized 的用法

synchronized 內(nèi)置鎖是一種對(duì)象鎖(鎖的是對(duì)象而非引用),作用粒度是對(duì)象,可以用來(lái)實(shí)現(xiàn)對(duì)臨界資源的同步互斥訪問(wèn),是可重入的。

2.1 修飾實(shí)例方法,鎖是當(dāng)前實(shí)例對(duì)象

public class Juc_LockOnThisObject {

    private Integer stock = 10;

    public synchronized void decrStock() {
        --stock;
        
        System.out.println(ClassLayout.parseInstance(this).toPrintable());
    }
}

synchronized 修飾非靜態(tài)方法鎖定的是該類(lèi)的實(shí)例,同一實(shí)例在多線程中調(diào)用才會(huì)觸發(fā)同步鎖定 所以多個(gè)被 synchronized 修飾的非靜態(tài)方法在同一實(shí)例下只能多線程同時(shí)調(diào)用一個(gè)。

2.2 修飾靜態(tài)方法,鎖是當(dāng)前類(lèi)對(duì)象

public class Juc_LockOnClass {

    private static int stock;
    
    public static synchronized void decrStock(){
        System.out.println(--stock);
    }
}

synchronized 修飾靜態(tài)方法鎖定的是類(lèi)本身,而不是實(shí)例,會(huì)作用于類(lèi)的所有對(duì)象實(shí)例 ,進(jìn)入同步代碼前要獲得當(dāng)前 class 的鎖。因?yàn)殪o態(tài)成員不屬于任何一個(gè)實(shí)例對(duì)象,是類(lèi)成員( static 表明這是該類(lèi)的一個(gè)靜態(tài)資源,不管 new 了多少個(gè)對(duì)象,只有一份)。所以,如果一個(gè)線程 A 調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài) synchronized 方法,而線程 B 需要調(diào)用這個(gè)實(shí)例對(duì)象所屬類(lèi)的靜態(tài) synchronized 方法,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問(wèn)靜態(tài) synchronized 方法占用的鎖是當(dāng)前類(lèi)的鎖,而訪問(wèn)非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對(duì)象鎖。

2.3 修飾代碼塊,鎖是括號(hào)里面的對(duì)象

public class Juc_LockOnObject {

    public static Object object = new Object();

    private Integer stock = 10;

    public void decrStock() {
        // T1,T2
        synchronized (object) {
            --stock;
            if (stock <= 0) {
                System.out.println("庫(kù)存售罄");
                return;
            }
        }
    }
}

synchronized 指定加鎖對(duì)象,對(duì)給定對(duì)象/類(lèi)加鎖。synchronized(this|object) 表示進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。synchronized(類(lèi).class) 表示進(jìn)入同步代碼前要獲得當(dāng)前 class 的鎖。

2.4 總結(jié)

  • synchronized 關(guān)鍵字加到 static 靜態(tài)方法和 synchronized(class) 代碼塊上都是是給 Class 類(lèi)上鎖。

  • synchronized 關(guān)鍵字加到實(shí)例方法上是給對(duì)象實(shí)例上鎖。

一起看一個(gè) synchronized 使用經(jīng)典實(shí)例—— 線程安全的單例模式:

public class LazySingleton {
    /**
     * 提供一個(gè)私有的靜態(tài)屬性,對(duì)于 volatile 修飾的字段,可以防止指令重排
     */
    private volatile static LazySingleton instance = null;

    /**
     * 提供一個(gè)私有構(gòu)造函數(shù):避免類(lèi)在外部被實(shí)例化
     */
    private LazySingleton() {

    }

    public static LazySingleton getInstance() {
        // 先判斷對(duì)象是否已經(jīng)實(shí)例過(guò),沒(méi)有實(shí)例化過(guò)才進(jìn)入加鎖代碼
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    // 類(lèi)對(duì)象加鎖
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

3. synchronized 的實(shí)現(xiàn)原理

3.1 synchronized 是怎么加鎖的?

synchronized 是基于 JVM內(nèi)置鎖 實(shí)現(xiàn),通過(guò)內(nèi)部對(duì)象 Monitor(監(jiān)視器鎖) 實(shí)現(xiàn),基于進(jìn)入與退出 Monitor 對(duì)象實(shí)現(xiàn)方法與代碼塊同步,監(jiān)視器鎖的實(shí)現(xiàn)依賴(lài)底層操作系統(tǒng)的 Mutex lock(互斥鎖) 實(shí)現(xiàn),它是一個(gè)重量級(jí)鎖,性能較低。當(dāng)然,JVM 內(nèi)置鎖在1.5之后版本做了大量的優(yōu)化,如鎖粗化(Lock Coarsening)、鎖消除(Lock Eliminaction)、輕量級(jí)鎖(Lightweight Locking)、偏向鎖(Biased Locking)、適應(yīng)性自旋(Adaptive Spinning)等技術(shù)來(lái)減少鎖操作的開(kāi)銷(xiāo),內(nèi)置鎖的并發(fā)性能已經(jīng)基本與 Lock 持平。

(1)synchronized 修飾代碼塊時(shí)

/**
 * @author pointer
 * @date 2023-04-21 21:16:47
 */
public class SynchronizedDemo {

    public void method() {
        synchronized (this) {
            System.out.println("synchronized 代碼塊");
        }
    }
}

通過(guò) JDK 自帶的 javap 命令查看 SynchronizedDemo 類(lèi)的相關(guān)字節(jié)碼信息:首先切換到類(lèi)的對(duì)應(yīng)目錄執(zhí)行 javac SynchronizedDemo.java 命令生成編譯后的 .class 文件,然后執(zhí)行 javap -c -s -v -l SynchronizedDemo.class。

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized
synchronized 修飾代碼塊時(shí),JVM 采用 monitorenter、monitorexit 兩個(gè)指令來(lái)實(shí)現(xiàn)同步,其中 monitorenter 指令指向同步代碼塊的開(kāi)始位置, monitorexit 指令則指向同步代碼塊的結(jié)束位置。

(2)synchronized 修飾實(shí)例方法

/**
 * @author pointer
 * @date 2023-04-21 21:16:47
 */
public class SynchronizedDemo {

    public synchronized void method() {
        System.out.println("synchronized 實(shí)例方法");
    }
}

反編譯一下:

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized

synchronized 修飾的方法并沒(méi)有 monitorenter 指令和 monitorexit 指令,取得代之的確實(shí)是 ACC_SYNCHRONIZED 標(biāo)識(shí),該標(biāo)識(shí)指明了該方法是一個(gè)同步方法。JVM 通過(guò)該 ACC_SYNCHRONIZED 訪問(wèn)標(biāo)志來(lái)辨別一個(gè)方法是否聲明為同步方法,從而執(zhí)行相應(yīng)的同步調(diào)用。

3.2 synchronized 同步概念

monitorenter、monitorexit 或者 ACC_SYNCHRONIZED 都是基于 Monitor(監(jiān)視器鎖) 實(shí)現(xiàn)的。

實(shí)例對(duì)象結(jié)構(gòu)里有對(duì)象頭,對(duì)象頭里面有一塊結(jié)構(gòu)叫 Mark Word,Mark Word 指針指向了 Monitor。

(1)Java 對(duì)象頭

在 HotSpot 虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding):

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized

synchronized 是悲觀鎖,在操作同步資源之前需要給同步資源先加鎖,這把鎖就是存在 Java對(duì)象頭 里的,Java對(duì)象頭是什么呢?

我們以 Hotspot 虛擬機(jī)為例,Hotspot 的對(duì)象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類(lèi)型指針):

Mark Word: Mark Word存儲(chǔ)對(duì)象自身的運(yùn)行數(shù)據(jù),如哈希碼、GC分代年齡、鎖狀態(tài)標(biāo)志、偏向時(shí)間戳(Epoch) 等信息。這些信息都是與對(duì)象自身定義無(wú)關(guān)的數(shù)據(jù),所以 Mark Word 被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲(chǔ)盡量多的數(shù)據(jù)。它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間,也就是說(shuō)在運(yùn)行期間 Mark Word 里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。

Klass Point: 對(duì)象指向它的類(lèi)元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例。

(2)監(jiān)視器鎖(Monitor)

監(jiān)視器鎖(Monitor) 本質(zhì)是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來(lái)實(shí)現(xiàn)的。每個(gè)對(duì)象都對(duì)應(yīng)于一個(gè)可稱(chēng)為 “互斥鎖” 的標(biāo)記,這個(gè)標(biāo)記用來(lái)保證在任一時(shí)刻,只能有一個(gè)線程訪問(wèn)該對(duì)象。

互斥鎖:用于保護(hù)臨界區(qū),確保同一時(shí)間只有一個(gè)線程訪問(wèn)數(shù)據(jù)。對(duì)共享資源的訪問(wèn),先對(duì)互斥量進(jìn)行加鎖,如果互斥量已經(jīng)上鎖,調(diào)用線程會(huì)阻塞,直到互斥量被解鎖。在完成了對(duì)共享資源的訪問(wèn)后,要對(duì)互斥量進(jìn)行解鎖。

  • mutex的工作方式:

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized

  1. 申請(qǐng) mutex;
  2. 如果成功,則持有該mutex;
  3. 如果失敗,則進(jìn)行spin自旋。spin的過(guò)程就是在線等待mutex,不斷發(fā)起mutex gets, 直到獲得mutex或者達(dá)到spin_count限制為止;
  4. 依據(jù)工作模式的不同選擇yiled還是sleep;
  5. 若達(dá)到sleep限制或者被主動(dòng)喚醒或者完成yield,則重復(fù)1-4 步,直到獲得為止。

由于Java的線程是映射到操作系統(tǒng)的原生線程之上的,如果要阻塞或喚醒一條線程,都需要操作系統(tǒng)來(lái)幫忙完成,這就需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài)中,因此狀態(tài)轉(zhuǎn)換需要耗費(fèi)很多的處理器時(shí)間。所以synchronized是Java語(yǔ)言中的一個(gè)重量級(jí)操作。在JDK1.6中,虛擬機(jī)進(jìn)行了一些優(yōu)化,譬如在通知操作系統(tǒng)阻塞線程之前加入一段自旋等待過(guò)程,避免頻繁地切入到核心態(tài)中;

synchronized與java.util.concurrent包中的ReentrantLock相比,由于JDK1.6中加入了針對(duì)鎖的優(yōu)化措施(見(jiàn)后面),使得synchronized與ReentrantLock的性能基本持平。ReentrantLock只是提供了synchronized更豐富的功能,而不一定有更優(yōu)的性能,所以在synchronized能實(shí)現(xiàn)需求的情況下,優(yōu)先考慮使用synchronized來(lái)進(jìn)行同步。

3.3 synchronized 的特性

3.3.1 原子性

原子性就是指一個(gè)操作或者多個(gè)操作,要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷,要么就都不執(zhí)行。被 synchronized 修飾的類(lèi)或?qū)ο蟮乃胁僮鞫际窃拥?,因?yàn)樵趫?zhí)行操作之前必須先獲得類(lèi)或?qū)ο蟮逆i,直到執(zhí)行完才能釋放。

3.3.2 可見(jiàn)性

可見(jiàn)性是指多個(gè)線程訪問(wèn)一個(gè)資源時(shí),該資源的狀態(tài)、值信息等對(duì)于其他線程都是可見(jiàn)的。synchronizedvolatile 都具有可見(jiàn)性。

synchronized怎么保證可見(jiàn)性?

  • 線程加鎖前,將清空工作內(nèi)存中共享變量的值,從而使用共享變量時(shí)需要從主內(nèi)存中重新讀取最新的值;

  • 線程加鎖后,其它線程無(wú)法獲取主內(nèi)存中的共享變量;

  • 線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存中。

3.3.3 有序性

有序性指的是對(duì)于一個(gè)線程的執(zhí)行代碼,從前往后依次執(zhí)行,單線程下可以認(rèn)為程序是有序的,但是并發(fā)時(shí)有可能會(huì)發(fā)生指令重排。

synchronized怎么保證有序性?

  • synchronized 同步的代碼塊,具有排他性,一次只能被一個(gè)線程擁有,所以 synchronized 保證同一時(shí)刻,代碼是單線程執(zhí)行的;

  • 因?yàn)?as-if-serial 語(yǔ)義的存在,單線程的程序能保證最終結(jié)果是有序的,但是不保證不會(huì)指令重排;

  • 所以 synchronized 保證的有序是執(zhí)行結(jié)果的有序性,而不是防止指令重排的有序性。

3.3.4 可重入

可重入特性指的是同一個(gè)線程獲得鎖之后,可以再次獲取該鎖(一個(gè)線程可以多次執(zhí)行 synchronized,重復(fù)獲取同一把鎖)。

public class NewThread {

    static class MyThread extends Thread {
        @Override
        public void run() {
            synchronized (NewThread.class) {
                System.out.println("我是 run");
            }

            test01();
        }

        void test01(){
            synchronized (NewThread.class) {
                System.out.println("我是 test01");
            }
        }
    }

    public static void main(String[] args) {
        MyThread myThread01 = new MyThread();
        MyThread myThread02 = new MyThread();

        myThread01.start();
        myThread02.start();
    }
}
  • 原理

synchronized 的鎖對(duì)象中有一個(gè)計(jì)數(shù)器(recursions變量)會(huì)記錄線程獲得幾次鎖,每重入一次,計(jì)數(shù)器就 + 1,在執(zhí)行完一個(gè)同步代碼塊時(shí),計(jì)數(shù)器數(shù)量就會(huì)減 1,直到計(jì)數(shù)器的數(shù)量為 0 才釋放這個(gè)鎖。

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

可以避免死鎖(如果不能重入,那就不能再次進(jìn)入這個(gè)同步代碼塊,導(dǎo)致死鎖);更好地封裝代碼(可以把同步代碼塊寫(xiě)入到一個(gè)方法中,然后在另一個(gè)同步代碼塊中直接調(diào)用該方法實(shí)現(xiàn)可重入)。

3.3.5 非公平

非公平是指多個(gè)線程在搶占鎖時(shí)JVM并不會(huì)保證線程先來(lái)后到的順序,非公平性可以提升吞吐量,因?yàn)樯倭司S護(hù)線程順序的開(kāi)銷(xiāo)。

4. 鎖優(yōu)化

synchronized 是通過(guò)對(duì)象內(nèi)部的一個(gè)叫做監(jiān)視器鎖(monitor)來(lái)實(shí)現(xiàn)的,監(jiān)視器鎖本質(zhì)又是依賴(lài)于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來(lái)實(shí)現(xiàn)的。而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,這就是為什么 synchronized 效率低的原因。因此,這種依賴(lài)于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱(chēng)之為 “重量級(jí)鎖”。

Java SE 1.6 為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗,引入了 “偏向鎖” 和 “輕量級(jí)鎖”:鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài)。鎖可以升級(jí)但不能降級(jí)。

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized

先看一下四種鎖狀態(tài)對(duì)應(yīng)的的Mark Word內(nèi)容,然后再分別講解四種鎖狀態(tài)的思路以及特點(diǎn):

鎖狀態(tài) 存儲(chǔ)內(nèi)容 存儲(chǔ)內(nèi)容
無(wú)鎖 對(duì)象的hashCode、對(duì)象分代年齡、是否是偏向鎖(0) 01
偏向鎖 偏向線程ID、偏向時(shí)間戳、對(duì)象分代年齡、是否是偏向鎖(1) 01
輕量級(jí)鎖 指向棧中鎖記錄的指針 00
重量級(jí)鎖 指向互斥量(重量級(jí)鎖)的指針 10

4.1 無(wú)鎖

無(wú)鎖沒(méi)有對(duì)資源進(jìn)行鎖定,所有的線程都能訪問(wèn)并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功。

無(wú)鎖的特點(diǎn)就是修改操作在循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源。如果沒(méi)有沖突就修改成功并退出,否則就會(huì)繼續(xù)循環(huán)嘗試。如果有多個(gè)線程修改同一個(gè)值,必定會(huì)有一個(gè)線程能修改成功,而其他修改失敗的線程會(huì)不斷重試直到修改成功。上面我們介紹的CAS原理及應(yīng)用即是無(wú)鎖的實(shí)現(xiàn)。無(wú)鎖無(wú)法全面代替有鎖,但無(wú)鎖在某些場(chǎng)合下的性能是非常高的。

4.2 偏向鎖

偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。在大多數(shù)情況下,鎖總是由同一線程多次獲得,不存在多線程競(jìng)爭(zhēng),所以出現(xiàn)了偏向鎖。其目標(biāo)就是在只有一個(gè)線程執(zhí)行同步代碼塊時(shí)能夠提高性能。

當(dāng)一個(gè)線程訪問(wèn)同步代碼塊并獲取鎖時(shí),會(huì)在Mark Word里存儲(chǔ)鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時(shí)不再通過(guò)CAS操作來(lái)加鎖和解鎖,而是檢測(cè)Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。引入偏向鎖是為了在無(wú)多線程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑,因?yàn)檩p量級(jí)鎖的獲取及釋放依賴(lài)多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時(shí)候依賴(lài)一次CAS原子指令即可。

偏向鎖只有遇到其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,線程不會(huì)主動(dòng)釋放偏向鎖。偏向鎖的撤銷(xiāo),需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài)。撤銷(xiāo)偏向鎖后恢復(fù)到無(wú)鎖(標(biāo)志位為“01”)或輕量級(jí)鎖(標(biāo)志位為“00”)的狀態(tài)。

偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的??梢酝ㄟ^(guò)JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。

4.3 輕量級(jí)鎖

是指當(dāng)鎖是偏向鎖的時(shí)候,被另外的線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。

在代碼進(jìn)入同步塊的時(shí)候,如果同步對(duì)象鎖狀態(tài)為無(wú)鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,然后拷貝對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。

拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為指向Lock Record的指針,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word。

如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,表示此對(duì)象處于輕量級(jí)鎖定狀態(tài)。

如果輕量級(jí)鎖的更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說(shuō)明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行,否則說(shuō)明多個(gè)線程競(jìng)爭(zhēng)鎖。

若當(dāng)前只有一個(gè)等待線程,則該線程通過(guò)自旋進(jìn)行等待。但是當(dāng)自旋超過(guò)一定的次數(shù),或者一個(gè)線程在持有鎖,一個(gè)在自旋,又有第三個(gè)來(lái)訪時(shí),輕量級(jí)鎖升級(jí)為重量級(jí)鎖。

4.4 重量級(jí)鎖

升級(jí)為重量級(jí)鎖時(shí),鎖標(biāo)志的狀態(tài)值變?yōu)?10,此時(shí)Mark Word中存儲(chǔ)的是指向重量級(jí)鎖的指針,此時(shí)等待鎖的線程都會(huì)進(jìn)入阻塞狀態(tài)。

4.5 鎖升級(jí)全過(guò)程

【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized
參考文章:
https://zhuanlan.zhihu.com/p/29866981 - Java synchronized原理總結(jié)
https://www.cnblogs.com/xfeiyun/p/15871647.html#gallery-6 - Java內(nèi)置同步鎖synchronized深入理解
https://www.cnblogs.com/three-fighter/p/14396208.html - synchronized詳解文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-434642.html

到了這里,關(guān)于【Java 并發(fā)編程】一文詳解 Java 內(nèi)置鎖 synchronized的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Java 并發(fā)編程】一文讀懂線程、協(xié)程、守護(hù)線程

    【Java 并發(fā)編程】一文讀懂線程、協(xié)程、守護(hù)線程

    在 Java 線程的生命周期一文中提到了 就緒狀態(tài)的線程在獲得 CPU 時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài) ,否則就會(huì)在 可運(yùn)行狀態(tài) 或者 阻塞狀態(tài) ,那么系統(tǒng)是如何分配線程時(shí)間片以及實(shí)現(xiàn)線程的調(diào)度的呢?下面我們就來(lái)講講線程的調(diào)度策略。 線程調(diào)度是指系統(tǒng)為線程分配 CPU 執(zhí)行時(shí)間片

    2023年04月08日
    瀏覽(24)
  • 并發(fā)-synchronized詳解

    并發(fā)-synchronized詳解

    JDK1.6之前的synchronized一來(lái)就直接給對(duì)象加了一把重量級(jí)鎖,頻繁地在用戶(hù)態(tài)和內(nèi)核態(tài)之間切換,導(dǎo)致性能非常低。為了彌補(bǔ)synchronized的不足,大佬doug lee寫(xiě)了一個(gè)AQS框架,用Java語(yǔ)言實(shí)現(xiàn)了ReentrantLock。然后在JDK1.6之后,oracle優(yōu)化了synchronized的鎖過(guò)程,增加了鎖的膨脹邏輯

    2024年02月12日
    瀏覽(14)
  • 【并發(fā)多線程】java并發(fā)中的Synchronized關(guān)鍵詞

    如果在多線程的環(huán)境中,我們經(jīng)常會(huì)遇到資源競(jìng)爭(zhēng)的情況,比如多個(gè)線程要去同時(shí)修改同一個(gè)共享變量,這時(shí)候,就需要對(duì)資源的訪問(wèn)方法進(jìn)行一定的處理,保證同一時(shí)間只有一個(gè)線程訪問(wèn)。 java提供了synchronized,方便我們實(shí)現(xiàn)上述操作。 我們舉個(gè)例子,我們創(chuàng)建一個(gè)

    2023年04月13日
    瀏覽(24)
  • JUC并發(fā)編程-線程和進(jìn)程、Synchronized 和 Lock、生產(chǎn)者和消費(fèi)者問(wèn)題

    JUC并發(fā)編程-線程和進(jìn)程、Synchronized 和 Lock、生產(chǎn)者和消費(fèi)者問(wèn)題

    源碼 + 官方文檔 面試高頻問(wèn)! java.util 工具包、包、分類(lèi) 業(yè)務(wù):普通的線程代碼 Thread Runnable Runnable 沒(méi)有返回值、效率相比入 Callable 相對(duì)較低! 線程、進(jìn)程,如果不能使用一句話(huà)說(shuō)出來(lái)的技術(shù),不扎實(shí)! 進(jìn)程:一個(gè)程序,QQ.exe Music.exe 程序的集合; 一個(gè)進(jìn)程往往可以包含多

    2024年01月20日
    瀏覽(20)
  • java并發(fā)編程 LinkedBlockingDeque詳解

    java 并發(fā)編程系列文章目錄 首先queue是一種數(shù)據(jù)結(jié)構(gòu),一個(gè)集合中,先進(jìn)后出,有兩種實(shí)現(xiàn)的方式,數(shù)組和鏈表。從尾部追加,從頭部獲取。Deque是兩端都可以添加,且兩端都可以獲取,所以它的方法會(huì)有一系列的Last,F(xiàn)rist語(yǔ)義,添加或獲取等操作會(huì)指明哪個(gè)方向的,這也是

    2024年02月10日
    瀏覽(21)
  • java并發(fā)編程:ArrayBlockingQueue詳解

    java并發(fā)編程:ArrayBlockingQueue詳解

    ArrayBlockingQueue 顧名思義:基于數(shù)組的阻塞隊(duì)列。數(shù)組是要指定長(zhǎng)度的,所以使用 ArrayBlockingQueue 時(shí)必須指定長(zhǎng)度,也就是它是一個(gè)有界隊(duì)列。它實(shí)現(xiàn)了 BlockingQueue 接口,有著隊(duì)列、集合以及阻塞隊(duì)列的所有方法。 ArrayBlockingQueue 是線程安全的,內(nèi)部使用 ReentrantLock 來(lái)保證。A

    2024年02月08日
    瀏覽(30)
  • java并發(fā)編程:LinkedBlockingQueue詳解

    java并發(fā)編程:LinkedBlockingQueue詳解

    在集合框架里,想必大家都用過(guò)ArrayList和LinkedList,也經(jīng)常在面試中問(wèn)到他們之間的區(qū)別。ArrayList和ArrayBlockingQueue一樣,內(nèi)部基于數(shù)組來(lái)存放元素,而LinkedBlockingQueue則和LinkedList一樣,內(nèi)部基于鏈表來(lái)存放元素。 LinkedBlockingQueue實(shí)現(xiàn)了BlockingQueue接口,這里放一張類(lèi)的繼承關(guān)系圖

    2024年02月08日
    瀏覽(64)
  • Java并發(fā)編程詳解:實(shí)現(xiàn)高效并發(fā)應(yīng)用的關(guān)鍵技術(shù)

    在當(dāng)前的計(jì)算機(jī)領(lǐng)域,高效的并發(fā)編程對(duì)于Java開(kāi)發(fā)人員而言變得越發(fā)重要。作為流行的編程語(yǔ)言,Java提供了強(qiáng)大的并發(fā)編程支持,使開(kāi)發(fā)人員能夠充分發(fā)揮多核處理器和線程的潛力,構(gòu)建高性能、高吞吐量的應(yīng)用程序。本文將深入探討Java并發(fā)編程的關(guān)鍵技術(shù),包括線程安全

    2024年02月13日
    瀏覽(30)
  • Java并發(fā)編程之線程池詳解

    Java并發(fā)編程之線程池詳解

    目錄 ??今日良言:不悲傷 不彷徨 有風(fēng)聽(tīng)風(fēng) 有雨看雨 ??一、簡(jiǎn)介 ??二、相關(guān)代碼 ??1.線程池代碼 ??2.自定義實(shí)現(xiàn)線程池 ??三、ThreadPoolExecutor類(lèi) 首先來(lái)介紹一下什么是線程池,線程池是一種利用池化技術(shù)思想來(lái)實(shí)現(xiàn)的線程管理技術(shù),主要是為了復(fù)用線程、便利地管理線程

    2024年02月12日
    瀏覽(24)
  • 【Java 并發(fā)編程】Java 線程本地變量 ThreadLocal 詳解

    【Java 并發(fā)編程】Java 線程本地變量 ThreadLocal 詳解

    先一起看一下 ThreadLocal 類(lèi)的官方解釋?zhuān)?用大白話(huà)翻譯過(guò)來(lái),大體的意思是: ThreadLoal 提供給了 線程局部變量 。同一個(gè) ThreadLocal 所包含的對(duì)象,在不同的 Thread 中有不同的副本。這里有幾點(diǎn)需要注意: 因?yàn)槊總€(gè) Thread 內(nèi)有自己的實(shí)例副本,且 該副本只能由當(dāng)前 Thread 使用 。

    2024年02月04日
    瀏覽(31)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包