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

深入講解Java?synchronized的核心原理

 更新時間:2023年07月18日 11:42:16   作者:我是小趴菜  
這篇文章主要為大家詳細介紹了Java中synchronized的核心原理以及簡單的用法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

前言

在此之前先有幾個面試題,看大家能答對幾題

1.1: 標準訪問ab二個線程,是先打印t1還是t2

public class SyncUnit {
    public synchronized void t1() {
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.2: t1方法暫停3秒鐘,是先打印t1還是t2

public class SyncUnit {
    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }

1.3: 新增一個普通方法hello(),是先打印t1還是hello

public class SyncUnit {
    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public void hello() {
        System.out.println("hello");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.hello();
        }).start();
    }
}

1.4: 現(xiàn)在有二個SyncUnit對象,是先打印t1還是t2

public class SyncUnit {
    public synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }

1.5: 二個靜態(tài)同步方法,一個SuncUnit對象,是先打印t1還是t2

public class SyncUnit {
    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public static synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.6: 二個靜態(tài)同步方法,二個SyncUnit對象,是先打印t1還是t2

public class SyncUnit {
    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public static synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }
}

1.7: 一個靜態(tài)同步方法,普通同步方法,一個SyncUnit對象,是先打印t1還是t2

public class SyncUnit {
    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit.t2();
        }).start();
    }
}

1.8 一個靜態(tài)同步方法,普通同步方法,二個SyncUnit對象,是先打印t1還是t2

public class SyncUnit {
    public static synchronized void t1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1");
    }
    public synchronized void t2() {
        System.out.println("t2");
    }
    public static void main(String[] args) throws Exception{
        SyncUnit syncUnit = new SyncUnit();
        SyncUnit syncUnit1 = new SyncUnit();
        new Thread(() -> {
            syncUnit.t1();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
           syncUnit1.t2();
        }).start();
    }
}

synchronized用法

synchronized是java提供的一種解決多線程并發(fā)安全的一種內(nèi)置鎖,盡管在jdk1.5之前還被大家吐槽性能問題,但是在1.5之后對synchronized不斷的優(yōu)化,在單機程序中,當設計多線程并發(fā)問題時,我們完全可以使用synchronized解決

同步實例方法

 public synchronized void method() {
      //方法邏輯
 }

當synchronized修飾的是一個普通方法的時候,相當于對this對象加鎖,一個實例是可以創(chuàng)建多個對象的,所以可以擁有多把鎖,就比如下面這個例子,我們創(chuàng)建了二個對象,那就是二把不同的鎖,所以在調用t1()的時候,t2()方法由于是不同的鎖,所以會直接執(zhí)行方法

public class SyncUnit {
        public synchronized void t1(){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1");
        }
        public synchronized void t2() {
            System.out.println("t2");
        }
        public static void main(String[] args) throws Exception{
            SyncUnit syncUnit = new SyncUnit();
            SyncUnit syncUnit1 = new SyncUnit();
            new Thread(() -> {
                syncUnit.t1();
            }).start();
            Thread.sleep(100);
            new Thread(() -> {
               syncUnit1.t2();
            }).start();
        }
    }

同步靜態(tài)方法

 public static synchronized void method() {
     //方法邏輯
 }

當synchronized修飾的是一個靜態(tài)方法的時候,相當于對當前實例加鎖,一個類只有一個實例,所以無論你創(chuàng)建多少個對象,都只有一把鎖,比如下面這個例子,雖然創(chuàng)建了二個不同的對象,但是實際只有一把鎖,所以是先打印t1(),然后在打印t2(),因為t2()要等待t1()把鎖釋放掉之后才能獲取到鎖

   public class SyncUnit {
            public static synchronized void t1(){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1");
            }
            public static synchronized void t2() {
                System.out.println("t2");
            }
            public static void main(String[] args) throws Exception{
                SyncUnit syncUnit = new SyncUnit();
                SyncUnit syncUnit1 = new SyncUnit();
                new Thread(() -> {
                    syncUnit.t1();
                }).start();
                Thread.sleep(100);
                new Thread(() -> {
                   syncUnit1.t2();
                }).start();
            }
       }

代碼塊

   public Object object = new Object();
   public void method() {
      synchronized(object) {
         //方法邏輯
      }
   }

這時候的object是一個對象,就相當于在普通方法上添加synchronized,如果是不同的對象,那么就是不同的鎖

      public void method() {
          synchronized(Test.class) {
             //方法邏輯
          }
       }

這時候就相當于在靜態(tài)方法上添加synchronized,也就是對當前實例加鎖,一個類只有一個實例

synchronized核心原理

synchornized是基于JVM中的Monitor鎖實現(xiàn)的,Java1.5版本之前的synchornized鎖性能較低,但是從1.6版本之后,對synchornized進行了大量的優(yōu)化,引入了鎖粗化,鎖消除,偏向鎖,輕量級鎖,適應性自旋等技術來提升synchornized的性能

1.synchornized修飾的是方法

當synchornized修飾的是方法的時候,當前方法會比普通方法多一個ACC_SYNCHRONIZED的標識符

當JVM執(zhí)行程序的時候,會判斷這個方法是否有ACC_SYNCHRONIZED這個標識符,如果有,則當前線程優(yōu)先獲取Monitor對象,同一個時刻只能有一個線程獲取到,在當前線程釋放Monitor對象之前,其它線程無法獲取到同一個Monitor對象,從而保證了同一時刻只能有一個線程進入到被synchornized修飾的方法

2.synchornized修飾的是代碼塊

當synchornized修飾的是代碼塊的時候,synchornized關鍵字會被編譯成monitorentermonitorexit,使得同一時刻只能有一個線程進入到同步代碼塊中,但是這里為什么會有二個monitorexit,是因為程序正常退出的時候需要釋放鎖,在程序異常的時候也要釋放鎖,所以會對應二個

無論synchornized修飾的是方法還是代碼塊,底層都是通過JVM調用操作系統(tǒng)的Mutes鎖實現(xiàn)的,當線程被阻塞時會被掛起,等待CPU重新調度,這會導致線程在操作系統(tǒng)的用戶態(tài)和內(nèi)核態(tài)之間切換,影響性能

Monitor鎖原理

synchornized低成是基于Monitor鎖來實現(xiàn)的,而Monitor鎖是基于操作系統(tǒng)的Mutex鎖實現(xiàn)的,Mutex鎖是操作系統(tǒng)級別的重量級鎖,所以性能較低

在Java中,創(chuàng)建的任何一個對象在JVM中都會關聯(lián)一個Monitor對象,所以說任何一個對象都可以成為鎖。

在HotSpot JVM中,Monitor是由ObjectMoitor實現(xiàn)的,在ObjectMonitor對象的數(shù)據(jù)結構中,有幾個重要的屬性

  • _WaitSet:是一個集合,當線程獲到鎖之后,但是還沒有完成業(yè)務邏輯,也還沒釋放鎖,這時候調用了Object類的wait()方法,這時候這個線程就會進入_WaitSet這個集合中等待被喚醒,也就是執(zhí)行nitify()或者notifyAll()方法喚醒
  • _EntryList:是一個集合,當有多個線程來獲取鎖,這時候只有一個線程能成功拿到鎖,剩下那些沒有拿到鎖的線程就會進入_EntryList集合中,等待下次搶鎖
  • _Owner:當一個線程獲取到鎖之后,就會將該值設置成當前線程,釋放鎖之后,這個值就會重新被設置成null
  • _count:當一個線程獲取到鎖之后,_count的值就會+1,釋放鎖之后就會-1,只有當減到0之后,才算真正的釋放掉鎖了,其它線程才能來獲取這把鎖,synchornized可重入鎖也是基于這個值來實現(xiàn)的

所以當多個線程同時訪問被synchornized修飾的方法或者代碼塊時候,synchornized加鎖和釋放鎖的底層實現(xiàn)流程大致為:

  • 1:進入_EntryList集合,當某個線程獲取到鎖之后,這個線程就會進入_Owner區(qū)域,就會將Monitor對象的_owner變量復制為當前線程。并把_count值+1
  • 2:當線程調用wait()方法時,當前線程會釋放掉持有的Monitor對象,并把_owner賦值成null,_count的值-1,同時這個線程就會進入_WaitSet集合等到被喚醒
  • 3:如果獲取到鎖的線程執(zhí)行完畢,也會釋放Monitor鎖。,_owner被置為null,_count被置為0

偏向鎖

雖然在程序的方法中或代碼塊中添加了synchornized,但是在大部分的情況下,不會存在多線程競爭這種情況,并且會出現(xiàn)同一個線程多次獲取同一把鎖的現(xiàn)象,為了提升這種情況下程序的性能,引入了偏向鎖

輕量級鎖

當多線程競爭鎖不激烈時,可以通過CAS機制競爭鎖,這就是輕量級鎖,引入輕量級鎖的目的是在多線程競爭鎖不激烈時,避免由于使用操作系統(tǒng)層面的Mutex重量級鎖導致性能低下

重量級鎖

重量級鎖主要是基于操作系統(tǒng)的Mutex鎖實現(xiàn),重量級鎖的執(zhí)行效率較低,處于重量級鎖時被阻塞的線程不會消耗CPU資源

鎖升級過程

多個線程在爭搶synchornized鎖時,在某些情況下,會由無鎖狀態(tài)一步步升級為最終的重量級鎖,整個升級過程大致包括如下幾個步驟

  • 1:線程在競爭synchornized時,JVM首先會檢查鎖對象的Mark Word中偏向鎖的標記位是否為1,鎖標記位是否為01,如果二個條件都滿足,則當前鎖處于偏向鎖狀態(tài)
  • 2:爭搶synchornized鎖線程檢查鎖對象的Mark Work中存儲的線程ID是否是自己的,如果是自己的線程ID,則表示處于偏向鎖狀態(tài),當前線程可以直接進入方法或者代碼塊
  • 3:如果鎖對象的Mark Word的線程ID不是自己的線程ID,那么就會通過CAS方式來競爭鎖資源,如果獲取到鎖資源了,就將Mark Word中存儲的線程ID修改成自己的線程ID,將偏向鎖的標記設置成1,鎖標記位置設置成01,當前鎖處于偏向鎖狀態(tài)
  • 4:如果當前線程通過CAS沒有獲取到鎖資源,則說明有其它線程也在爭搶資源,此時會撤銷偏向鎖,升級為輕量級鎖,并將Mark Word的鎖標記為都清空
  • 5:當前線程與其它線程還是會通過CAS方式來競爭資源,如果某個線程成功獲取到資源,就會將鎖對象的Mark Word中的鎖標志位設置成00,此時進入輕量級鎖狀態(tài)
  • 6:競爭失敗的線程還是會通過CAS方式來獲取鎖,但是當CAS達到一定的次數(shù)以后,就會升級為重量級鎖了

以上就是深入講解Java synchronized的核心原理的詳細內(nèi)容,更多關于Java synchronized的資料請關注腳本之家其它相關文章!

相關文章

  • SpringBoot項目實現(xiàn)Excel上傳功能的示例代碼

    SpringBoot項目實現(xiàn)Excel上傳功能的示例代碼

    這篇文章主要為大家詳細介紹了SpringBoot項目中實現(xiàn)Excel上傳功能的相關方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-09-09
  • 在分布式環(huán)境下正確使用MyBatis二級緩存的最佳實踐

    在分布式環(huán)境下正確使用MyBatis二級緩存的最佳實踐

    緩存就是內(nèi)存中的數(shù)據(jù),常常來自對數(shù)據(jù)庫查詢結果的保存,使用緩存,我們可以避免頻繁與數(shù)據(jù)庫進行交互,從而提高響應速度,這篇文章主要介紹了在分布式環(huán)境下正確使用MyBatis二級緩存的最佳實踐,需要的朋友可以參考下
    2025-08-08
  • 解決Swagger修改請求對象字段文檔不更新問題

    解決Swagger修改請求對象字段文檔不更新問題

    這篇文章主要為大家介紹了解決Swagger修改請求對象字段文檔不更新的問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • springboot?集成activemq項目配置方法

    springboot?集成activemq項目配置方法

    這篇文章主要介紹了springboot?集成activemq項目配置方法,e-car項目配置通過引入activemq依賴,本文結合實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • Spring Cloud使用Feign進行遠程調用的操作指南

    Spring Cloud使用Feign進行遠程調用的操作指南

    本文介紹了Feign作為聲明式HTTP客戶端在SpringCloud中的使用,從簡介、對比RestTemplate的問題、使用步驟,到日志配置、性能優(yōu)化和實際應用進行了詳細講解,包括如何通過Feign簡化接口調用,以及解決啟動時找不到FeignClient的問題,需要的朋友可以參考下
    2025-02-02
  • 學習Java的Date、Calendar日期操作

    學習Java的Date、Calendar日期操作

    Java開發(fā)過程中避免不了日期相關操作,這篇文章總結了一些Date、Calendar的常用方法,需要的朋友可以參考下
    2015-07-07
  • Java項目中添加外部jar包的兩種方式(收藏版)

    Java項目中添加外部jar包的兩種方式(收藏版)

    這篇文章主要介紹了java項目中添加外部jar包的兩種方式,第二種方式是將外部jar包引入到本地maven倉庫中,本文給大家講解的非常詳細,需要的朋友可以參考下
    2023-03-03
  • JavaSE網(wǎng)絡原理之UDP和TCP原理詳解

    JavaSE網(wǎng)絡原理之UDP和TCP原理詳解

    TCP、UDP都是屬于運輸層的協(xié)議,提供端到端的進程之間的邏輯通信,而IP協(xié)議是提供主機間的邏輯通信,應用層規(guī)定應用進程在通信時所遵循的協(xié)議,這篇文章主要介紹了JavaSE網(wǎng)絡原理之UDP和TCP原理的相關資料,需要的朋友可以參考下
    2026-01-01
  • Java中的RabbitMQ使用場景和實踐完全指南

    Java中的RabbitMQ使用場景和實踐完全指南

    本文涵蓋了RabbitMQ在Java中的主要使用場景和實踐方法,在實際應用中,還需要根據(jù)具體的業(yè)務需求和系統(tǒng)架構進行適當?shù)恼{整和優(yōu)化,本文結合代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2025-08-08
  • Java 是如何利用接口避免函數(shù)回調的方法

    Java 是如何利用接口避免函數(shù)回調的方法

    本篇文章主要介紹了Java 是如何利用接口避免函數(shù)回調的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02

最新評論

宣威市| 克东县| 睢宁县| 蓝田县| 盘山县| 泸溪县| 油尖旺区| 韶山市| 沛县| 乐亭县| 铁岭市| 广西| 阳城县| 高淳县| 宜丰县| 于田县| 高邑县| 沿河| 西乌珠穆沁旗| 上高县| 通州区| 普兰店市| 方正县| 延长县| 平凉市| 安远县| 托克托县| 大宁县| 普兰店市| 东莞市| 恩施市| 友谊县| 贺州市| 绥化市| 靖远县| 长武县| 哈尔滨市| 安龙县| 泰宁县| 徐州市| 上饶市|