亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

Java中怎么利用synchronized實現多線程鎖

發布時間:2021-06-21 15:18:43 來源:億速云 閱讀:164 作者:Leah 欄目:大數據

這篇文章給大家介紹Java中怎么利用synchronized實現多線程鎖,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

synchronized的四種使用方式

  1. 修飾代碼塊:被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號{}括起來的代碼,作用于調用對象

  2. 修飾方法:被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用于調用對象 > 注意:synchronized修飾方法時必須是顯式調用,如果沒有顯式調用,例如子類重寫該方法時沒有顯式加上synchronized,則不會有加鎖效果。

  3. 修飾靜態方法:其作用的范圍是整個靜態方法,作用于所有對象

  4. 修飾類:其作用的范圍是synchronized后面括號括起來的部分(例如:test.class),作用于所有對象

對象鎖和類鎖是否會互相影響么?

  • 對象鎖:Java的所有對象都含有1個互斥鎖,這個鎖由JVM自動獲取和釋放。線程進入synchronized方法的時候獲取該對象的鎖,當然如果已經有線程獲取了這個對象的鎖,那么當前線程會等待;synchronized方法正常返回或者拋異常而終止,JVM會自動釋放對象鎖。這里也體現了用synchronized來加鎖的1個好處,方法拋異常的時候,鎖仍然可以由JVM來自動釋放。

  • 類鎖:對象鎖是用來控制實例方法之間的同步,類鎖是用來控制靜態方法(或靜態變量互斥體)之間的同步。其實類鎖只是一個概念上的東西,并不是真實存在的,它只是用來幫助我們理解鎖定實例方法和靜態方法的區別的。java類可能會有很多個對象,但是只有1個Class對象,也就是說類的不同實例之間共享該類的Class對象。Class對象其實也僅僅是1個java對象,只不過有點特殊而已。由于每個java對象都有1個互斥鎖,而類的靜態方法是需要Class對象。所以所謂的類鎖,不過是Class對象的鎖而已。

類鎖和對象鎖不是同1個東西,一個是類的Class對象的鎖,一個是類的實例的鎖。也就是說:1個線程訪問靜態synchronized的時候,允許另一個線程訪問對象的實例synchronized方法。反過來也是成立的,因為他們需要的鎖是不同的。

對應的實驗代碼如下:

@Slf4j
public class SynchronizedExample {

    // 修飾一個代碼塊
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }

    // 修飾一個方法
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    // 修飾一個類
    public static void test3(int j) {
        synchronized (SynchronizedExample.class) {
            for (int i = 0; i < 10; i++) {
                log.info("test3 {} - {}", j, i);
            }
        }
    }

    // 修飾一個靜態方法
    public static synchronized void test4(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test4 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample example1 = new SynchronizedExample();
        SynchronizedExample example2 = new SynchronizedExample();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}

在JDK1.6之前,synchronized一直被稱呼為重量級鎖(重量級鎖就是采用互斥量來控制對資源的訪問)。通過反編譯成字節碼指令可以看到,synchronized會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節碼指令。根據虛擬機規范的要求,在執行monitorenter指令時,首先要嘗試獲取對象的鎖。如果這個對象沒被鎖定,或者當前線程已經擁有了那個對象的鎖,把鎖的計數器加1,相應的,在執行monitorexit指令時會將鎖計算器減1,當計數器為0時,鎖就被釋放,然后notify通知所有等待的線程。 Java的線程是映射到操作系統的原生線程上的,如果要阻塞或喚醒一個線程,都需要操作系統來幫忙完成,這就需要用戶態和內核態切換,大量的狀態轉換需要耗費很多處理器的時間。

synchronized的優化

在JDK1.6中對鎖的實現引入了大量的優化:

  1. 鎖粗化(Lock Coarsening):將多個連續的鎖擴展成一個范圍更大的鎖,用以減少頻繁互斥同步導致的性能損耗。

  2. 鎖消除(Lock Elimination):JVM即時編譯器在運行時,通過逃逸分析,如果判斷一段代碼中,堆上的所有數據不會逃逸出去從來被其他線程訪問到,就可以去除這個鎖。

  3. 偏向鎖(Biased Locking):目的是消除數據無競爭情況下的同步原語。使用CAS記錄獲取它的線程。下一次同一個線程進入則偏向該線程,無需任何同步操作。

  4. 適應性自旋(Adaptive Spinning):為了避免線程頻繁掛起、恢復的狀態切換消耗。線程會進入自旋狀態。JDK1.6引入了自適應自旋。自旋時間根據之前鎖自旋時間和線程狀態,動態變化,可以能減少自旋的時間。

  5. 輕量級鎖(Lightweight Locking):在沒有多線程競爭的情況下避免重量級互斥鎖,只需要依靠一條CAS原子指令就可以完成鎖的獲取及釋放。

在JDK1.6之后,synchronized不再是重量級鎖,鎖的狀態變成以下四種狀態:
無鎖->偏向鎖->輕量級鎖->重量級鎖

鎖的狀態

自適應自旋鎖

大部分時候,共享數據的鎖定狀態只會持續很短的一段時間,為了這段時間去掛起和恢復線程并不值得。如果能讓兩個或以上的線程同時并行執行,我們就可以讓后面請求鎖的那個線程“稍等一下”,但不放棄處理器的執行時間,看看持有鎖的線程是否很快就會釋放鎖。這項技術就是所謂的自旋鎖。
自旋等待的時間必須要有一定的限度,如果自旋超過了限定的次數仍然沒有獲取到鎖,則該線程應該被掛起。在JDK1.6中引入了自適應的自旋鎖,自適應意味著自旋的時間不再固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。

> 所謂自旋,不是獲取不到就阻塞,而是在原地等待一會兒,再次嘗試(當然次數或者時長有限),他是以犧牲CPU為代價來換取內核狀態切換帶來的開銷。借助于適應性自旋,可以在CPU時間片的損耗和內核狀態的切換開銷之間相對的找到一個平衡,進而能夠提高性能

偏向鎖

大多數情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊并獲取鎖時,會在對象頭的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單地測試一下對象頭的MarkWord里是否存儲著指向當前線程的偏向鎖。如果測試成功,表示線程已經獲得了鎖。如果測試失敗,則需要再測試一下MarkWord中偏向鎖的標識是否設置成1(表示當前是偏向鎖):如果沒有設置,則使用CAS競爭鎖;如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程,如果失敗則進行輕量鎖的升級。

Java中怎么利用synchronized實現多線程鎖

輕量級鎖

如果說偏向鎖是只允許一個線程獲得鎖,那么輕量級鎖就是允許多個線程獲得鎖,但是只允許他們順序拿鎖,不允許出現競爭,也就是拿鎖失敗的情況,輕量級鎖的步驟如下:

  1. 線程1在執行同步代碼塊之前,JVM會先在當前線程的棧幀中創建一個空間用來存儲鎖記錄,然后再把對象頭中的MarkWord復制到該鎖記錄中,官方稱之為DisplacedMarkWord。然后線程嘗試使用CAS將對象頭中的MarkWord替換為指向鎖記錄的指針。如果成功,則獲得鎖,進入步驟3)。如果失敗執行步驟2)

  2. 線程自旋,自旋成功則獲得鎖,進入步驟3)。自旋失敗,則膨脹成為重量級鎖,并把鎖標志位變為10,線程阻塞進入步驟3)

  3. 鎖的持有線程執行同步代碼,執行完CAS替換MarkWord成功釋放鎖,如果CAS成功則流程結束,CAS失敗執行步驟4)

  4. CAS執行失敗說明期間有線程嘗試獲得鎖并自旋失敗,輕量級鎖升級為了重量級鎖,此時釋放鎖之后,還要喚醒等待的線程

Java中怎么利用synchronized實現多線程鎖

重量級鎖

自旋的線程在自旋過程中,成功獲得資源(即之前獲的資源的線程執行完成并釋放了共享資源),則整個狀態依然處于輕量級鎖的狀態,如果自旋失敗則進入重量級鎖的狀態,這個時候,自旋的線程進行阻塞,等待之前線程執行完成并喚醒自己,需要從用戶態切換到內核態實現。(當競爭競爭激烈時,線程直接進入阻塞狀態。不過在高版本的JVM中不會立刻進入阻塞狀態而是會自旋一小會兒看是否能獲取鎖如果不能則進入阻塞狀態。)

總結

可以簡單總結是如下場景:

  1. 只有一個線程進入加鎖區,鎖狀態是偏向鎖

  2. 多個線程交替進入加鎖區,鎖狀態可能是輕量級鎖

  3. 多線程同時進入加鎖區,鎖狀態可能是重量級鎖

關于Java中怎么利用synchronized實現多線程鎖就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

吐鲁番市| 卓资县| 双城市| 彭水| 尚义县| 成武县| 横峰县| 沈丘县| 梓潼县| 台前县| 家居| 荔浦县| 忻州市| 会宁县| 砚山县| 西乌珠穆沁旗| 宁波市| 云龙县| 定州市| 肇庆市| 贡觉县| 陇南市| 阿图什市| 万全县| 临泉县| 浦城县| 九寨沟县| 双辽市| 岢岚县| 呼和浩特市| 博罗县| 佛山市| 河南省| 法库县| 湟源县| 赫章县| 章丘市| 秦皇岛市| 谢通门县| 泌阳县| 波密县|