亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

在此之前先有幾個(gè)面試題,看大家能答對(duì)幾題

1.1: 標(biāo)準(zhǔn)訪問(wèn)ab二個(gè)線程,是先打印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: 新增一個(gè)普通方法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)在有二個(gè)SyncUnit對(duì)象,是先打印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: 二個(gè)靜態(tài)同步方法,一個(gè)SuncUnit對(duì)象,是先打印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: 二個(gè)靜態(tài)同步方法,二個(gè)SyncUnit對(duì)象,是先打印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: 一個(gè)靜態(tài)同步方法,普通同步方法,一個(gè)SyncUnit對(duì)象,是先打印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 一個(gè)靜態(tài)同步方法,普通同步方法,二個(gè)SyncUnit對(duì)象,是先打印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之前還被大家吐槽性能問(wèn)題,但是在1.5之后對(duì)synchronized不斷的優(yōu)化,在單機(jī)程序中,當(dāng)設(shè)計(jì)多線程并發(fā)問(wèn)題時(shí),我們完全可以使用synchronized解決

同步實(shí)例方法

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

當(dāng)synchronized修飾的是一個(gè)普通方法的時(shí)候,相當(dāng)于對(duì)this對(duì)象加鎖,一個(gè)實(shí)例是可以創(chuàng)建多個(gè)對(duì)象的,所以可以擁有多把鎖,就比如下面這個(gè)例子,我們創(chuàng)建了二個(gè)對(duì)象,那就是二把不同的鎖,所以在調(diào)用t1()的時(shí)候,t2()方法由于是不同的鎖,所以會(huì)直接執(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() {
     //方法邏輯
 }

當(dāng)synchronized修飾的是一個(gè)靜態(tài)方法的時(shí)候,相當(dāng)于對(duì)當(dāng)前實(shí)例加鎖,一個(gè)類只有一個(gè)實(shí)例,所以無(wú)論你創(chuàng)建多少個(gè)對(duì)象,都只有一把鎖,比如下面這個(gè)例子,雖然創(chuàng)建了二個(gè)不同的對(duì)象,但是實(shí)際只有一把鎖,所以是先打印t1(),然后在打印t2(),因?yàn)閠2()要等待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) {
         //方法邏輯
      }
   }

這時(shí)候的object是一個(gè)對(duì)象,就相當(dāng)于在普通方法上添加synchronized,如果是不同的對(duì)象,那么就是不同的鎖

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

這時(shí)候就相當(dāng)于在靜態(tài)方法上添加synchronized,也就是對(duì)當(dāng)前實(shí)例加鎖,一個(gè)類只有一個(gè)實(shí)例

synchronized核心原理

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

1.synchornized修飾的是方法

當(dāng)synchornized修飾的是方法的時(shí)候,當(dāng)前方法會(huì)比普通方法多一個(gè)ACC_SYNCHRONIZED的標(biāo)識(shí)符

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

2.synchornized修飾的是代碼塊

當(dāng)synchornized修飾的是代碼塊的時(shí)候,synchornized關(guān)鍵字會(huì)被編譯成monitorentermonitorexit,使得同一時(shí)刻只能有一個(gè)線程進(jìn)入到同步代碼塊中,但是這里為什么會(huì)有二個(gè)monitorexit,是因?yàn)槌绦蛘M顺龅臅r(shí)候需要釋放鎖,在程序異常的時(shí)候也要釋放鎖,所以會(huì)對(duì)應(yīng)二個(gè)

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

Monitor鎖原理

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

在Java中,創(chuàng)建的任何一個(gè)對(duì)象在JVM中都會(huì)關(guān)聯(lián)一個(gè)Monitor對(duì)象,所以說(shuō)任何一個(gè)對(duì)象都可以成為鎖。

在HotSpot JVM中,Monitor是由ObjectMoitor實(shí)現(xiàn)的,在ObjectMonitor對(duì)象的數(shù)據(jù)結(jié)構(gòu)中,有幾個(gè)重要的屬性

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

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

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

偏向鎖

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

輕量級(jí)鎖

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

重量級(jí)鎖

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

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

多個(gè)線程在爭(zhēng)搶synchornized鎖時(shí),在某些情況下,會(huì)由無(wú)鎖狀態(tài)一步步升級(jí)為最終的重量級(jí)鎖,整個(gè)升級(jí)過(guò)程大致包括如下幾個(gè)步驟

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

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

相關(guān)文章

  • 深入JVM剖析Java的線程堆棧

    深入JVM剖析Java的線程堆棧

    這篇文章主要介紹了深入JVM剖析Java的線程堆棧,Java中的堆內(nèi)存和堆棧原理的應(yīng)用等知識(shí)是深入學(xué)習(xí)Java的重點(diǎn),需要的朋友可以參考下
    2015-07-07
  • jpa介紹以及在spring boot中使用詳解

    jpa介紹以及在spring boot中使用詳解

    最近在項(xiàng)目中使用了一下jpa,發(fā)現(xiàn)還是挺好用的。這里就來(lái)講一下jpa以及在spring boot中的使用。在這里我們先來(lái)了解一下jpa,希望能給你帶來(lái)幫助
    2021-08-08
  • Spring異常捕獲且回滾事務(wù)解決方案

    Spring異常捕獲且回滾事務(wù)解決方案

    這篇文章主要介紹了Spring異常捕獲且回滾事務(wù)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • maven父子工程多模塊統(tǒng)一管理版本號(hào)的解決方法

    maven父子工程多模塊統(tǒng)一管理版本號(hào)的解決方法

    maven父子工程多模塊,每個(gè)模塊還都可以獨(dú)立存在,子模塊往往通常希望和父工程保持一樣的版本,如果每個(gè)工程單獨(dú)定義版本號(hào),后期變更打包也非常麻煩,,所以本文給大家介紹了maven父子工程多模塊如何管理統(tǒng)一的版本號(hào),需要的朋友可以參考下
    2024-09-09
  • Jrebel啟動(dòng)失敗解決方案詳解

    Jrebel啟動(dòng)失敗解決方案詳解

    這篇文章主要介紹了Jrebel啟動(dòng)失敗解決方案詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • springboot配置https訪問(wèn)的方法

    springboot配置https訪問(wèn)的方法

    這篇文章主要介紹了springboot配置https訪問(wèn)的方法,需要的朋友可以參考下
    2018-11-11
  • Java concurrency線程池之線程池原理(一)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java concurrency線程池之線程池原理(一)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了Java concurrency線程池之線程池原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • HashMap原理的深入理解

    HashMap原理的深入理解

    這篇文章主要介紹了對(duì)HashMap原理的理解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Spring?Cloud?Sleuth?和?Zipkin?進(jìn)行分布式跟蹤使用小結(jié)

    Spring?Cloud?Sleuth?和?Zipkin?進(jìn)行分布式跟蹤使用小結(jié)

    分布式跟蹤是一種機(jī)制,我們可以使用它跟蹤整個(gè)分布式系統(tǒng)中的特定請(qǐng)求,分布式跟蹤允許您跟蹤分布式系統(tǒng)中的請(qǐng)求,本文給大家介紹Spring?Cloud?Sleuth?和?Zipkin?進(jìn)行分布式跟蹤使用小結(jié),感興趣的朋友一起看看吧
    2022-03-03
  • java中獲取當(dāng)前服務(wù)器的Ip地址的方法

    java中獲取當(dāng)前服務(wù)器的Ip地址的方法

    本篇文章主要介紹了java中獲取當(dāng)前服務(wù)器的Ip地址的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02

最新評(píng)論