溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Synchronized的原理是什么

發(fā)布時間:2021-06-29 09:56:25 來源:億速云 閱讀:119 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Synchronized的原理是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Synchronized的原理是什么”吧!

一、對象鎖、類鎖

當(dāng)synchronized修飾普通方法、修飾某個對象、修飾this時,是對象鎖,只有相同的對象可以訪問。

當(dāng)synchronized修飾靜態(tài)方法、修飾.class時,是類鎖,同一個類的不同實例都可以訪問。

public class Test {

    Object o1=new Object();
    Object o2=new Object();
	
    public synchronized void test1() {
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }
    public void test2() {
        synchronized (this){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
    public static synchronized void test3() {
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }
    public void test4() {
        synchronized (Test.class){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
    public void test5() {
        synchronized (o1){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
    public void test6() {
        synchronized (o2){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
    public void test7() {
        synchronized (o2){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }
    public static void main(String[] args) {
        final Test myt1 = new Test();
		final Test myt2 = new Test();
        Thread test1 = new Thread(new Runnable() {
            @Override
            public void run() {
                myt.test1();
            }
        }, "test1");
        Thread test2 = new Thread(new Runnable() {
            @Override
            public void run() {
                myt.test2();
            }
        }, "test2");
        test1.start();
        test2.start();
    }
}

當(dāng)兩個線程都使用myt1這個對象去訪問方法時:

1,2是同一把鎖(會產(chǎn)生鎖的競爭);3,4是同一把鎖;6,7是同一把鎖;他們相互之間是不同的鎖。

當(dāng)兩個線程分別使用myt1和myt2去訪問方法時:

1,2是不同的鎖(因為myt1和myt2是不同的對象);3,4是同一把鎖(因為myt1和myt2是相同的類);6,7是不同的鎖。

二、sychronized原理

對象鎖的實現(xiàn)使用的是monitorenter 和 monitorexit 指令,其中monitorenter指令指向同步代碼塊的開始位置,monitorexit指令則指明同步代碼塊的結(jié)束位置,當(dāng)執(zhí)行monitorenter指令時,當(dāng)前線程將試圖獲取 objectref(即對象鎖) 所對應(yīng)的 monitor 的持有權(quán),當(dāng) objectref 的 monitor 的進(jìn)入計數(shù)器為 0,那線程可以成功取得 monitor,并將計數(shù)器值設(shè)置為 1,取鎖成功。如果當(dāng)前線程已經(jīng)擁有 objectref 的 monitor 的持有權(quán),那它可以重入這個 monitor (關(guān)于重入性稍后會分析),重入時計數(shù)器的值也會加 1。倘若其他線程已經(jīng)擁有 objectref 的 monitor 的所有權(quán),那當(dāng)前線程將被阻塞,直到正在執(zhí)行線程執(zhí)行完畢,即monitorexit指令被執(zhí)行,執(zhí)行線程將釋放 monitor(鎖)并設(shè)置計數(shù)器值為0 ,其他線程將有機(jī)會持有 monitor 。

而monitor的本質(zhì)是操作系統(tǒng)的互斥鎖(Mutex Lock)

java中每個對象都可以成為鎖,是因為java對象頭的設(shè)計:

對象頭含有三部分:Mark Word(存儲對象自身運(yùn)行時數(shù)據(jù))、Class Metadata Address(存儲類元數(shù)據(jù)的指針)、Array length(數(shù)組長度,只有數(shù)組類型才有)。重點在Mark Word部分,Mark Word數(shù)據(jù)結(jié)構(gòu)被設(shè)計成非固定的,會隨著對象的不同狀態(tài)而變化,如下表所示:

Synchronized的原理是什么

Synchronized的原理是什么

2、拷貝對象頭中的Mark Word復(fù)制到鎖記錄中;

3、拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針,并將Lock record里的owner指針指向object mark word。如果更新成功,則執(zhí)行步驟4,否則執(zhí)行步驟5。

4、如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標(biāo)志位設(shè)置為“00”,即表示此對象處于輕量級鎖定狀態(tài),這時候線程堆棧與對象頭的狀態(tài)如圖所示。

5、如果這個更新操作失敗了,虛擬機(jī)首先會檢查對象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行。否則說明多個線程競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,后面等待鎖的線程也要進(jìn)入阻塞狀態(tài)。 而當(dāng)前線程便嘗試使用自旋來獲取鎖,自旋就是為了不讓線程阻塞,而采用循環(huán)去獲取鎖的過程。

輕量級鎖釋放:

之前在獲取鎖的時候它拷貝了鎖對象頭的markword,在釋放鎖的時候如果它發(fā)現(xiàn)在它持有鎖的期間有其他線程來嘗試獲取鎖了,并且該線程對markword做了修改,兩者比對發(fā)現(xiàn)不一致,則切換到重量鎖。

總結(jié)加鎖流程:

  1. 檢測Mark Word里面是不是當(dāng)前線程的ID,如果是,表示當(dāng)前線程處于偏向鎖

  2. 如果不是,則使用CAS將當(dāng)前線程的ID替換Mard Word,如果成功則表示當(dāng)前線程獲得偏向鎖,置偏向標(biāo)志位1

  3. 如果失敗,則說明發(fā)生競爭,撤銷偏向鎖,進(jìn)而升級為輕量級鎖。

  4. 當(dāng)前線程使用CAS將對象頭的Mark Word替換為鎖記錄指針,如果成功,當(dāng)前線程獲得鎖

  5. 如果失敗,表示其他線程競爭鎖,當(dāng)前線程便嘗試使用自旋來獲取鎖。

  6. 如果自旋成功則依然處于輕量級狀態(tài)。

  7. 如果自旋失敗,則升級為重量級鎖

https://blog.csdn.net/zqz_zqz/article/details/70233767

https://blog.csdn.net/u012465296/article/details/53022317

https://www.cnblogs.com/javaminer/p/3889023.html

到此,相信大家對“Synchronized的原理是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI