真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java中的synchronized是什么

這篇文章主要介紹了java中的synchronized是什么的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇java中的synchronized是什么文章都會有所收獲,下面我們一起來看看吧。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供陽東網(wǎng)站建設(shè)、陽東做網(wǎng)站、陽東網(wǎng)站設(shè)計、陽東網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、陽東企業(yè)網(wǎng)站模板建站服務(wù),10年陽東做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

一、synchronized實現(xiàn)鎖的表現(xiàn)形式

  1. 修飾實例方法,對于普通同步方法,鎖是當(dāng)前的實例對象

  2. 修飾靜態(tài)方法,對于靜態(tài)同步方法,鎖是當(dāng)前的Class對象

  3. 修飾方法代碼塊,對于同步方法塊,鎖是synchronized括號里面配置的對象!

當(dāng)一個線程試圖訪問同步代碼塊的時候,就必須得到鎖,完成后(或者出現(xiàn)異常),就必須釋放鎖。那么鎖究竟存在什么地方呢?我們一塊來探究!

不過,相信,既然大家能夠找到這篇文章,相信大家對他的使用早已了熟于心,我們對于使用,以及為什么多線程情況下,數(shù)據(jù)會出現(xiàn)錯亂情況,不做詳細(xì)的解釋!只把他的幾種使用方式列出,供參考!

①修飾實例方法

修飾實例方法,對于普通同步方法,鎖是當(dāng)前的實例對象

這個沒得說,使用的同一個實例,添加上synchronized后,線程需要排隊,完成一個原子操作,但是注意前提是使用的同一個實例,他才會生效!

正例:

/**
* @author huangfu
*/
public class ExploringSynchronized implements Runnable {
   /**
    * 共享資源(臨界資源)
    */
   static int i=0;
   public synchronized void add(){
       i++;
   }

   @Override
   public void run() {
       for (int j = 0; j < 100000; j++) {
           add();
       }
   }

   public static void main(String[] args) throws InterruptedException {
       ExploringSynchronized exploringSynchronized = new ExploringSynchronized();
       Thread t1 = new Thread(exploringSynchronized);
       Thread t2 = new Thread(exploringSynchronized);
       t1.start();
       t2.start();
       //join 主線程需要等待子線程完成后在結(jié)束
       t1.join();
       t2.join();
       System.out.println(i);

   }
}

反例:

/**
* @author huangfu
*/
public class ExploringSynchronized implements Runnable {
   /**
    * 共享資源(臨界資源)
    */
   static int i=0;
   public synchronized void add(){
       i++;
   }

   @Override
   public void run() {
       for (int j = 0; j < 100000; j++) {
           add();
       }
   }

   public static void main(String[] args) throws InterruptedException {
       Thread t1 = new Thread(new ExploringSynchronized());
       Thread t2 = new Thread(new ExploringSynchronized());
       t1.start();
       t2.start();
       //join 主線程需要等待子線程完成后在結(jié)束
       t1.join();
       t2.join();
       System.out.println(i);

   }
}

這種,即使你在方法上加上了synchronized也無濟于事,因為,對于普通同步方法,鎖是當(dāng)前的實例對象!實例對象都不一樣了,那么他們之間的鎖自然也就不是同一個!

②修飾靜態(tài)方法

修飾靜態(tài)方法,對于靜態(tài)同步方法,鎖是當(dāng)前的Class對象

從定義上可以看出來,他的鎖是類對象,那么也就是說,以上面那個類為例:普通方法的鎖對象是 new ExploringSynchronized()而靜態(tài)方法對應(yīng)的鎖對象是ExploringSynchronized.class所以對于靜態(tài)方法添加同步鎖,即使你重新創(chuàng)建一個實例,它拿到的鎖還是同一個!

package com.byit.test;

/**
* @author huangfu
*/
public class ExploringSynchronized implements Runnable {
   /**
    * 共享資源(臨界資源)
    */
   static int i=0;
   public synchronized static void add(){
       i++;
   }

   @Override
   public void run() {
       for (int j = 0; j < 100000; j++) {
           add();
       }
   }

   public static void main(String[] args) throws InterruptedException {
       Thread t1 = new Thread(new ExploringSynchronized());
       Thread t2 = new Thread(new ExploringSynchronized());
       t1.start();
       t2.start();
       //join 主線程需要等待子線程完成后在結(jié)束
       t1.join();
       t2.join();
       System.out.println(i);

   }
}

當(dāng)然,結(jié)果是我們期待的  200000

③修飾方法代碼塊

修飾方法代碼塊,對于同步方法塊,鎖是synchronized括號里面配置的對象!

package com.byit.test;

/**
* @author huangfu
*/
public class ExploringSynchronized implements Runnable {
   /**
    * 鎖標(biāo)記
    */
   private static final String LOCK_MARK = "LOCK_MARK";
   /**
    * 共享資源(臨界資源)
    */
   static int i=0;
   public void add(){
       synchronized (LOCK_MARK){
           i++;
       }
   }

   @Override
   public void run() {
       for (int j = 0; j < 100000; j++) {
           add();
       }
   }

   public static void main(String[] args) throws InterruptedException {
       Thread t1 = new Thread(new ExploringSynchronized());
       Thread t2 = new Thread(new ExploringSynchronized());
       t1.start();
       t2.start();
       //join 主線程需要等待子線程完成后在結(jié)束
       t1.join();
       t2.join();
       System.out.println(i);

   }
}

對于同步代碼塊,括號里面是什么,鎖對象就是什么,里面可以使用this  字符串  對象等等!

二、synchronized的底層實現(xiàn)

java中synchronized的實現(xiàn)是基于進(jìn)入和退出的 Monitor對象實現(xiàn)的,無論是顯式同步(修飾代碼塊,有明確的monitorentermonitorexit指令)還是隱式同步(修飾方法體)!

需要注意的是,只有修飾代碼塊的時候,才是基于monitorentermonitorexit指令來實現(xiàn)的;修飾方法的時候,是通過另一種方式實現(xiàn)的!我會放到后面去說!

在了解整個實現(xiàn)底層之前,我還是希望你能夠大致了解一下對象在內(nèi)存中的結(jié)構(gòu)詳情!

java中的synchronized是什么

  • 實例變量:存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息,如果是數(shù)組的實例部分還包括數(shù)組的長度,這部分內(nèi)存按4字節(jié)對齊。

  • 填充數(shù)據(jù):由于虛擬機要求對象起始地址必須是8字節(jié)的整數(shù)倍。填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對齊,這點了解即可。

這兩個概念,我們簡單理解就好!我們今天并不去探究對象的構(gòu)成原理!我們著重探究一下對象頭,他對我們理解鎖尤為重要!

一般而言,synchronized使用的鎖存在于對象頭里面!如果是數(shù)組對象,則虛擬機使用3個字寬存儲對象,如果是非數(shù)組對象,則使用兩個字寬存儲對象頭!字虛擬機里面1字寬等于4字節(jié)!主要結(jié)構(gòu)是 Mark WordClass Metadata Address組成,結(jié)構(gòu)如下:

虛擬機位數(shù)頭對象結(jié)構(gòu)說明
32/64bitMark Word存儲對象的hashCode、鎖信息或分代年齡或GC標(biāo)志等信息
32/64bitClass Metadata Address存儲到隊形類型數(shù)據(jù)的指針
32/64bit(數(shù)組)Aarray length數(shù)組的長度

通過上述表格能夠看出  鎖信息 存在于 Mark Word  內(nèi),那么 Mark Word 內(nèi)又是如何組成的呢?

鎖狀態(tài)25bit4bit1bit是否是偏向鎖2bit鎖標(biāo)志位
無鎖狀態(tài)對象的hashcode對象的分代年齡001

在運行起見,mark Word 里存儲的數(shù)據(jù)會隨著鎖的標(biāo)志位的變化而變化。mark Word可能變化為存儲一下四種數(shù)據(jù)

java中的synchronized是什么

Java SE 1.6為了減少獲得鎖和釋放鎖帶來的消耗,引入了偏向鎖輕量級鎖,從之前上來就是重量級鎖到1.6之后,鎖膨脹升級的優(yōu)化,極大地提高了synchronized的效率;

鎖一共有4中狀態(tài),級別從低到高:

java中的synchronized是什么

這幾個狀態(tài)會隨著鎖的競爭,逐漸升級。鎖可以升級,但是不能降級,其根本的原因就是為了提高獲取鎖和釋放鎖的效率!

那么,synchronized是又如何保證的線程安全的呢?或許我們需要從字節(jié)碼尋找答案!

package com.byit.test;

/**
* @author Administrator
*/
public class SynText {
   private static String A = "a";
   public int i ;

   public void add(){
       synchronized (A){
           i++;
       }

   }
}

反編譯的字節(jié)碼

Compiled from "SynText.java"
public class com.byit.test.SynText {
 public int i;

 public com.byit.test.SynText();
   Code:
      0: aload_0
      1: invokespecial #1                  // Method java/lang/Object."":()V
      4: return

 public void add();
   Code:
      0: getstatic     #2                  // Field A:Ljava/lang/String;
      3: dup
      4: astore_1
      5: monitorenter
      6: aload_0
      7: dup
      8: getfield      #3                  // Field i:I
     11: iconst_1
     12: iadd
     13: putfield      #3                  // Field i:I
     16: aload_1
     17: monitorexit
     18: goto          26
     21: astore_2
     22: aload_1
     23: monitorexit
     24: aload_2
     25: athrow
     26: return
   Exception table:
      from    to  target type
          6    18    21   any
         21    24    21   any

 static {};
   Code:
      0: ldc           #4                  // String a
      2: putstatic     #2                  // Field A:Ljava/lang/String;
      5: return
}

省去不必要的,簡化在簡化

   5: monitorenter
     ...
     17: monitorexit
     ...
     23: monitorexit

從字節(jié)碼中可知同步語句塊的實現(xiàn)使用的是monitorentermonitorexit指令,其中monitorenter指令指向同步代碼塊的開始位置,monitorexit指令則指明同步代碼塊的結(jié)束位置,當(dāng)執(zhí)行monitorenter指令的時候,線程將試圖獲取對象所所對應(yīng)的monitor特權(quán),當(dāng)monitor的的計數(shù)器為0的時候,線程就可以獲取monitor,并將計數(shù)器設(shè)置為1.去鎖成功!如果當(dāng)前線程已經(jīng)擁有monitor特權(quán),則可以直接進(jìn)入方法(可重入鎖),計數(shù)器+1;如果其他線程已經(jīng)擁有了monitor特權(quán),那么本縣城將會阻塞!

擁有monitor特權(quán)的線程執(zhí)行完成后釋放monitor,并將計數(shù)器設(shè)置為0;同時執(zhí)行monitorexit指令;不要擔(dān)心出現(xiàn)異常無法執(zhí)行monitorexit指令;為了保證在方法異常完成時 monitorenter 和 monitorexit 指令依然可以正確配對執(zhí)行,編譯器會自動產(chǎn)生一個異常處理器,這個異常處理器聲明可處理所有的異常,它的目的就是用來執(zhí)行 monitorexit 指令。從字節(jié)碼中也可以看出多了一個monitorexit指令,它就是異常結(jié)束時被執(zhí)行的釋放monitor 的指令。

同步代碼塊的原理了解了,那么同步方法如何解釋?不急,我們不妨來反編譯一下同步方法的狀態(tài)!

javap -verbose -p SynText > 3.txt

代碼

package com.byit.test;

/**
* @author huangfu
*/
public class SynText {
   public int i ;

   public synchronized void add(){
       i++;

   }
}

字節(jié)碼

Classfile /D:/2020project/byit-myth-job/demo-client/byit-demo-client/target/classes/com/byit/test/SynText.class
 Last modified 2020-1-6; size 382 bytes
 MD5 checksum e06926a20f28772b8377a940b0a4984f
 Compiled from "SynText.java"
public class com.byit.test.SynText
 minor version: 0
 major version: 52
 flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
  #1 = Methodref          #4.#17         // java/lang/Object."":()V
  #2 = Fieldref           #3.#18         // com/byit/test/SynText.i:I
  #3 = Class              #19            // com/byit/test/SynText
  #4 = Class              #20            // java/lang/Object
  #5 = Utf8               i
  #6 = Utf8               I
  #7 = Utf8              
  #8 = Utf8               ()V
  #9 = Utf8               Code
 #10 = Utf8               LineNumberTable
 #11 = Utf8               LocalVariableTable
 #12 = Utf8               this
 #13 = Utf8               Lcom/byit/test/SynText;
 #14 = Utf8               syncTask
 #15 = Utf8               SourceFile
 #16 = Utf8               SynText.java
 #17 = NameAndType        #7:#8          // "":()V
 #18 = NameAndType        #5:#6          // i:I
 #19 = Utf8               com/byit/test/SynText
 #20 = Utf8               java/lang/Object
{
 public int i;
   descriptor: I
   flags: ACC_PUBLIC

 public com.byit.test.SynText();
   descriptor: ()V
   flags: ACC_PUBLIC
   Code:
     stack=1, locals=1, args_size=1
        0: aload_0
        1: invokespecial #1                  // Method java/lang/Object."":()V
        4: return
     LineNumberTable:
       line 6: 0
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0       5     0  this   Lcom/byit/test/SynText;

 public synchronized void syncTask();
   descriptor: ()V
   flags: ACC_PUBLIC, ACC_SYNCHRONIZED
   Code:
     stack=3, locals=1, args_size=1
        0: aload_0
        1: dup
        2: getfield      #2                  // Field i:I
        5: iconst_1
        6: iadd
        7: putfield      #2                  // Field i:I
       10: return
     LineNumberTable:
       line 10: 0
       line 11: 10
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0      11     0  this   Lcom/byit/test/SynText;
}
SourceFile: "SynText.java"

簡化,在簡化

 public synchronized void syncTask();
   descriptor: ()V
   flags: ACC_PUBLIC, ACC_SYNCHRONIZED
   Code:
     stack=3, locals=1, args_size=1
        0: aload_0
        1: dup

我們能夠看到 flags: ACC_PUBLIC, ACC_SYNCHRONIZED這樣的一句話

從字節(jié)碼中可以看出,synchronized修飾的方法并沒有monitorenter指令和monitorexit指令,取得代之的確實是ACC_SYNCHRONIZED標(biāo)識,該標(biāo)識指明了該方法是一個同步方法,JVM通過該ACC_SYNCHRONIZED訪問標(biāo)志來辨別一個方法是否聲明為同步方法,從而執(zhí)行相應(yīng)的同步調(diào)用。這便是synchronized鎖在同步代碼塊和同步方法上實現(xiàn)的基本原理。

那么在JAVA6之前,為什么synchronized會如此的慢?

那是因為,操作系統(tǒng)實現(xiàn)線程之間的切換需要系統(tǒng)內(nèi)核從用戶態(tài)切換到核心態(tài)!這個狀態(tài)之間的轉(zhuǎn)換,需要較長的時間,時間成本高!所以這也就是synchronized慢的原因!

三、鎖膨脹的過程

在這之前,你需要知道什么是鎖膨脹!他是JAVA6之后新增的一個概念!是一種針對之前重量級鎖的一種性能的優(yōu)化!他的優(yōu)化,大部分是基于經(jīng)驗上的一些感官,對鎖來進(jìn)行優(yōu)化!

①偏向鎖

研究發(fā)現(xiàn),大多數(shù)情況下,鎖不僅不存在多線程競爭,而且還總是由一條線程獲得!因為為了減少鎖申請的次數(shù)!引進(jìn)了偏向鎖!在沒有鎖競爭的情況下,如果一個線程獲取到了鎖,那么鎖就進(jìn)入偏向鎖的模式!當(dāng)線程再一次請求鎖時,無需申請,直接獲取鎖,進(jìn)入方法!但是前提是沒有鎖競爭的情況,存在鎖競爭,鎖會立即膨脹,膨脹為輕量級鎖!

②輕量級鎖

偏向鎖失敗,那么鎖膨脹為輕量級鎖!此時鎖機構(gòu)變?yōu)檩p量級鎖結(jié)構(gòu)!他的經(jīng)驗依據(jù)是:“絕大多數(shù)情況下,在整個同步周期內(nèi),不會存在鎖的競爭”,故而,輕量級鎖適合,線程交替進(jìn)行的場景!如果在同一時間出現(xiàn)兩條線程對同一把鎖的競爭,那么此時輕量級鎖就不會生效了!但是,jdk官方為了是鎖的優(yōu)化性能更好,輕量級鎖失效后,并不會立即膨脹為重量級鎖!而是將鎖轉(zhuǎn)換為自旋鎖狀態(tài)!

③自旋鎖

輕量級鎖失敗后,為了是避免線程掛起,引起內(nèi)核態(tài)的切換!為了優(yōu)化,此時線程會進(jìn)入自選狀態(tài)!他可能會進(jìn)行幾十次,上百次的空輪訓(xùn)!為什么呢?又是經(jīng)驗之談!他們認(rèn)為,大多數(shù)情況下,線程持有鎖的時間都不會太長!做幾次空輪訓(xùn),就能大概率的等待到鎖!事實證明,這種優(yōu)化方式確實有效!最后如果實在等不到鎖!沒辦法,才會徹底升級為重量級鎖!

④鎖消除

jvm在進(jìn)行代碼編譯時,會基于上下文掃描;將一些不可能存在資源競爭的的鎖給消除掉!這也是JVM對于鎖的一種優(yōu)化方式!不得不感嘆,jdk官方的腦子!舉個例子!在方法體類的局部變量對象,他永遠(yuǎn)也不可能會發(fā)生鎖競爭,例如:

/**
* @author huangfu
*/
public class SynText {
   public static void add(String name1 ,String name2){
       StringBuffer sb = new StringBuffer();
       sb.append(name1).append(name2);
   }

   public static void main(String[] args) {
       for (int i = 0; i < 10000000; i++) {
           add("w"+i,"q"+i);
       }
   }
}

不能否認(rèn),StringBuffer是線程安全的!但是他永遠(yuǎn)也不會被其他線程引用!故而,鎖失效!故而,被消除掉!

關(guān)于“java中的synchronized是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“java中的synchronized是什么”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


分享題目:java中的synchronized是什么
文章地址:http://weahome.cn/article/pjpscd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部