Synchronized原理

[TOC]

作用

是一把能夠保證在同一時(shí)刻最多只有一個(gè)線程執(zhí)行該段代碼的jdk內(nèi)置同步鎖,可以達(dá)到保證并發(fā)安全的效果

使用方式

修飾代碼塊

public void syncCodeBlock(){
    synchronized (this){
        System.out.println("synchronized 修飾代碼塊");
    }
}

字節(jié)碼

 0 aload_0
 1 dup
 2 astore_1
 // 進(jìn)入同步塊
 3 monitorenter
 4 aload_1
 // 退出同步塊
 5 monitorexit
 6 goto 14 (+8)
 9 astore_2
10 aload_1
11 monitorexit
12 aload_2
13 athrow
14 return

修飾方法

public synchronized void syncMethod(){
    System.out.println("synchronized 修飾方法");
}

字節(jié)碼

在方法內(nèi)部的指令中沒有任何變化,只是在方法的訪問標(biāo)志上多了個(gè) synchronized

image

原理

Synchronized關(guān)鍵字是操作鎖對(duì)象的對(duì)象的markword來(lái)實(shí)現(xiàn)同步和鎖的升級(jí)的源哩。

鎖對(duì)象

不同的修飾方式鎖住的對(duì)象也不同放祟。

  1. 修飾靜態(tài)方法,鎖的對(duì)象是 Class對(duì)象
  2. 修飾實(shí)例方法, 鎖的this對(duì)象
  3. 代碼塊中 synchronized(某個(gè)對(duì)象),Class對(duì)象或者任何類的實(shí)例對(duì)象都可以

對(duì)象布局

java對(duì)象在jvm中以8字節(jié)的整數(shù)倍存在,

包含 對(duì)象頭和實(shí)例數(shù)據(jù) 和對(duì)齊填充數(shù)據(jù)

基本對(duì)象布局

對(duì)象頭布局

對(duì)象頭由markword和klasspoint組成

對(duì)象頭布局
  • markword : 包含了鎖的信息,gc信息,hashcode等
  • Klasspoint : 存儲(chǔ)的是指向?qū)ο笤獢?shù)據(jù)的指針

markword

jvm源碼中對(duì)64位系統(tǒng)中對(duì)象頭markword的布局解釋是這樣的

markOop.hpp :

//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)

偏向標(biāo)識(shí) :biased_lock :1

1bit 偏向標(biāo)識(shí)
0 不可偏向
1 可偏向

鎖標(biāo)識(shí) :lock:2

2bit 鎖標(biāo)識(shí)
00 輕量鎖
01 無(wú)鎖
10 重量

不同的對(duì)象由于鎖狀態(tài)的不同 對(duì)象頭鎖存儲(chǔ)的信息也會(huì)不同

image

不同鎖狀態(tài)下的markword

jol-core jar

可以借助這個(gè)工具打印對(duì)象的布局信息

maven 依賴 :

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.13</version>
    <scope>provided</scope>
</dependency>

1. 無(wú)鎖可偏向

// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)

無(wú)鎖可偏向markword
示例代碼
public static void main(String[] args) throws InterruptedException {
    A a = new A();
    log.error(ClassLayout.parseInstance(a).toPrintable(a));
}
打印結(jié)果
image

由于筆記本是小端模式,高位字節(jié)在后,低位字節(jié)在前,反過來(lái)正好是這樣的

無(wú)鎖可偏向markword

2. 無(wú)鎖不可偏向

由于偏向鎖需要記錄線程id,以標(biāo)識(shí)鎖偏向于哪個(gè)線程。線程id需要54位+2位 偏向時(shí)間戳記錄在markword中,但是對(duì)象生成了hashcode同樣記錄在markword中,導(dǎo)致線程id

沒有空間記錄,所以

對(duì)象生成了hashcode之后是 不可偏向的

無(wú)鎖不可偏向markword
示例代碼

調(diào)用對(duì)象的hashcode方法

public static void main(String[] args) throws InterruptedException {
    A a = new A();
    a.hashCode();
    log.error(ClassLayout.parseInstance(a).toPrintable(a));
}
輸出結(jié)果
image

反轉(zhuǎn)小端模式, 最后8 是 0000 0001 ,發(fā)現(xiàn)生成了hashcode之后,是否可偏向標(biāo)識(shí)已經(jīng) 被改成了 0,表示不可偏向

無(wú)鎖不可偏向markword

3. 已偏向

當(dāng)對(duì)象沒有生成hashcode,并且jvm沒有禁用偏向鎖,那么當(dāng)?shù)谝粋€(gè)線程持有鎖的時(shí)候,就會(huì)在鎖對(duì)象的markword中記錄下線程id:54位 + epoch : 2

已偏向markword
實(shí)例代碼
public static void main(String[] args) throws InterruptedException {
    A a = new A();
    // 線程持有過鎖
    new Thread(()->{
        synchronized (a){
            System.out.println(111);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    Thread.sleep(2000);
    log.error(ClassLayout.parseInstance(a).toPrintable(a));
}
輸出結(jié)果
image

最后面八位依然是 0 0000對(duì)象年齡 1 可偏向 01 輕量鎖

只不過前面56位已經(jīng)變成 線程id:54 + epoch:2了

即使偏向鎖釋放了,這些信息還在, 為了下一個(gè)加鎖判斷是否是偏向自己

已偏向markword

4. 輕量鎖

輕量鎖升級(jí) :當(dāng)鎖被A線程持有之后刷晋,并且成為偏向鎖,不管A線程有沒有釋放鎖, 只要B線程來(lái)持有鎖,發(fā)現(xiàn)鎖對(duì)象有線程id,并且不等于自己的線程id,就會(huì)撤銷偏向鎖,升級(jí)為輕量鎖。

此時(shí),鎖狀態(tài)會(huì)變?yōu)?01,并且前62位存儲(chǔ)的會(huì)是 B線程私有棧中的lock_record對(duì)象的指針福压。

輕量鎖持有中markword

輕量鎖釋放后:會(huì)清除markword中線程私有棧的lock_record指針

輕量鎖被釋放后markword
示例代碼

先用A線程將鎖升級(jí)為偏向鎖,再B線程加鎖,在同步代碼塊中以后B線程釋放鎖之后,打印對(duì)象頭信息

    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        // 沒有被任何線程加鎖,打印對(duì)象頭信息
        log.error(ClassLayout.parseInstance(a).toPrintable(a));
        // 升級(jí)為偏向鎖 :第一個(gè)線程持有鎖
        new Thread(()->{
            synchronized (a){
            }
        }).start();
        Thread.sleep(3000);
        // 偏向鎖釋放后掏秩,打印對(duì)象頭信息
        log.error(ClassLayout.parseInstance(a).toPrintable(a));
        // 升級(jí)為輕量鎖 :另一個(gè)線程在前一個(gè)線程釋放鎖之后加鎖
        new Thread(()->{
            synchronized (a){
                // 同步代碼塊中 輕量鎖持有的時(shí)候,打印對(duì)象頭信息
               log.error(ClassLayout.parseInstance(a).toPrintable(a));
            }
        }).start();
        Thread.sleep(3000);
        // 輕量鎖釋放后,打印對(duì)象頭
        log.error(ClassLayout.parseInstance(a).toPrintable(a));
    }
輸出結(jié)果
image

5. 重量鎖

Block_lock : 2 變成 10

鎖釋放后還是markword里的Monitor對(duì)象指針不會(huì)被清空

重量鎖markword

鎖的膨脹過程(源碼解析)

1. 無(wú)鎖

當(dāng)一個(gè)對(duì)象被創(chuàng)建出來(lái),沒有被任何一個(gè)對(duì)象作為Synchroinize鎖對(duì)象,那么就是無(wú)鎖狀態(tài)。對(duì)象頭后三位 : 1 01

2. 偏向鎖

已偏向&偏向自己

markword中,線程id有值,但是epoch值有效,表示只有一個(gè)線程持有這個(gè)鎖,并且鎖偏向于這個(gè)線程

偏向的線程再次拿鎖,判斷鎖對(duì)象中線程id是不是自己,是的話直接拿到偏向鎖

//當(dāng)前線程是偏向鎖并且偏向自己
if(anticipated_bias_locking_value == 0) {
  if (PrintBiasedLockingStatistics) {
                (* BiasedLocking::biased_lock_entry_count_addr())++;
              }
              success = true;
    
// 如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖荆姆,success不是true蒙幻,直接嘗試升級(jí)為輕量鎖
}else if(anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
              // try revoke bias
              // Class的對(duì)象頭(此時(shí)已經(jīng)被第40次改成禁用偏向了)
              markOop header = lockee->klass()->prototype_header();
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              // 如果鎖對(duì)象的markword沒有改變,cas將class對(duì)象的markword設(shè)置到鎖對(duì)象中
              if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
    
    
//  重偏向邏輯
}else if(anticipated_bias_locking_value & epoch_mask_in_place) !=0){
  ......
// 匿名偏向邏輯
else {
   ......
}

匿名偏向

這個(gè)狀態(tài)下鎖對(duì)象的markword中沒有線程id,意味著該鎖不偏向任何線程,加鎖的線程cas設(shè)置鎖的線程id后,即可獲取鎖,進(jìn)行業(yè)務(wù)代碼的執(zhí)行

cas失敗的情況

對(duì)象頭被更改,包括鎖狀態(tài),hashcode生成都會(huì)失敗,會(huì)升級(jí)為重量鎖

源碼
//當(dāng)前線程是自己,重偏向鎖
if(){
  ....
    
    
// 如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖,success不是true
}else if(){
  ....
    
    
//  偏向鎖過期(批量重偏向),lockee類的對(duì)象作為偏向鎖,在被其他線程升級(jí)到輕量鎖的過程中,會(huì)先撤銷偏向鎖再升級(jí)為輕量鎖,
// 如果這個(gè)過程超過20次,會(huì)將lockee類的鎖對(duì)象都設(shè)置為可重偏向(markword的線程id位設(shè)置為a線程的線程id)
}else if(){
  // 重偏向的邏輯
}
// 重點(diǎn)看這里
else {
    // try to bias towards thread in case object is anonymously biased
    // 匿名偏向
    // 先獲取之前的對(duì)象頭  00000000 101
    markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                    epoch_mask_in_place));
   // hash一開始初始化為 沒有hash的狀態(tài)
              // 如果在升級(jí)偏向鎖的過程中,被其他線程生成了hashcode,那么之前上面這行代碼里獲取header就會(huì)有hash值,替換成無(wú)hash的狀態(tài),那么 在下面的cas過程中,無(wú)hash的header和內(nèi)存地址中有hash的header就會(huì)不一樣,就會(huì)cas失敗,升級(jí)為重量鎖
    if (hash != markOopDesc::no_hash) {
      header = header->copy_set_hash(hash);
    }
    // 新建一個(gè)對(duì)象頭,里面的線程id換成當(dāng)前線程id
    markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
    // 省略jvm debug代碼
    ....
     // cas,如果之前的對(duì)象頭 和現(xiàn)在鎖對(duì)象的markword相同(沒有其他線程改變鎖的狀態(tài),也沒有生成hash),則替換成功,獲取偏向鎖成功
    if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
      if (PrintBiasedLockingStatistics)
        (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
    }
    else {
      // 否則升級(jí)為重量鎖
      CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
    }
    success = true;
  }
}

可重偏向(偏向撤銷20以上)

markword中,線程id有值,但是epoch值無(wú)效,表示 該偏向鎖已經(jīng)過期,可以重新重新偏向于另外一個(gè)線程

原因

lockee類的對(duì)象作為偏向鎖,在被其他線程升級(jí)到輕量鎖的過程中,會(huì)先撤銷偏向鎖再升級(jí)為輕量鎖,
如果這個(gè)過程超過20次,epoch值就會(huì)無(wú)效,這時(shí)再加鎖,會(huì)將lockee類的鎖對(duì)象都設(shè)置為可重偏向(markword的原線程id位設(shè)置為當(dāng)前加鎖線程的線程id)

則會(huì)生成偏向于當(dāng)前線程的markword,cas替換鎖對(duì)象的markword

示例代碼
    public static void main(String[] args) throws InterruptedException {
        List<A> list = new ArrayList<>();
        log.error("==============t1================");
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 30; i++) {
                A a = new A();
                log.error("{} ,{},加鎖前: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
                synchronized (a){
                    log.error("{} ,{},加鎖中: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
                }
                log.error("{} ,{},加鎖后: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
                list.add(a);
            }

        });
        t1.setName("t1");
        t1.start();
        t1.join();
        log.error("===================t2====================");
        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= list.size(); i++) {
                A a = list.get(i-1);
                log.error("{} ,{},加鎖前: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
                synchronized (a){
                    log.error("{} ,{},加鎖中: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
                }
                log.error("{} ,{},加鎖后: {}",Thread.currentThread().getName(),i, ClassLayout.parseInstance(a).toPrintableTest(a));
            }
        });
        t2.setName("t2");
        t2.start();
        t2.join();

    }
image

發(fā)現(xiàn)胆筒,t2到了20次的時(shí)候邮破,也就是經(jīng)歷了20次的偏向撤銷****,那么再次獲取鎖的時(shí)候仆救,又變成了偏向鎖抒和。并且偏向t2。

jvm源碼
//當(dāng)前線程是自己,重偏向鎖
if(){
  ....
    
    
// 如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖彤蔽,success不是true
}else if(){
  ....
    
    
//  偏向鎖過期(批量重偏向),lockee類的對(duì)象作為偏向鎖,在被其他線程升級(jí)到輕量鎖的過程中,會(huì)先撤銷偏向鎖再升級(jí)為輕量鎖,
// 如果這個(gè)過程超過20次,會(huì)將lockee類的鎖對(duì)象都設(shè)置為可重偏向(markword的線程id位設(shè)置為a線程的線程id)
}else if(anticipated_bias_locking_value & epoch_mask_in_place) !=0){
  // 重偏向的邏輯
  // try rebias
              // 創(chuàng)建偏向當(dāng)前線程的markword
              markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
                        // 這里鎖hashcode被其他線程生成,header改變,cas會(huì)失敗,升級(jí)為重量鎖
              if (hash != markOopDesc::no_hash) {
                new_header = new_header->copy_set_hash(hash);
              }
              // cas 替換markword
              if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
              }
              else {
                // cas失敗膨脹至重量鎖
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
}
// 匿名偏向邏輯
else {
    
}

禁用偏向

如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖摧莽,success不是true,直接嘗試升級(jí)為輕量鎖

實(shí)例代碼:

    public static void main(String[] args) throws InterruptedException {
        List<A> list = new ArrayList<>();
        log.error("==============t1================");
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 40; i++) {
                A a = new A();
                log.error("{} ,{},加鎖前: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                synchronized (a) {
                    log.error("{} ,{},加鎖中: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                }
                log.error("{} ,{},加鎖后: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                list.add(a);
            }
        });
        t1.setName("t1");
        t1.start();
        t1.join();
        log.error("===================t2====================");
        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 40; i++) {
                A a = list.get(i - 1);
                log.error("{} ,{},加鎖前: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                synchronized (a) {
                    log.error("{} ,{},加鎖中: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                }
                log.error("{} ,{},加鎖后: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
            }
        });
        t2.setName("t2");
        t2.start();
        t2.join();
        //注意當(dāng)t2執(zhí)行完成的時(shí)候顿痪,其實(shí)也才經(jīng)歷了20次的偏向撤銷镊辕,因?yàn)閠2執(zhí)行的時(shí)候油够,后面20次,走的都是重偏向的流程
        //此時(shí)征懈,list中石咬,前20個(gè)存的是輕量鎖對(duì)象,后20個(gè)存的是偏向t2的偏向鎖對(duì)象卖哎。
        log.error("==================t3=======================");
        Thread t3 = new Thread(() -> {
            for (int i = 1; i <= 40; i++) {
                A a = list.get(i - 1);
                log.error("{} ,{},加鎖前: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                synchronized (a) {
                    log.error("{} ,{},加鎖中: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
                }
                log.error("{} ,{},加鎖后: {}", Thread.currentThread().getName(), i, ClassLayout.parseInstance(a).toPrintableTest(a));
            }
        });
        t3.setName("t3");
        t3.start();
        t3.join();
        //當(dāng)t3執(zhí)行完成鬼悠,把list中后面20個(gè)偏向t2的對(duì)象,經(jīng)過偏向撤銷升級(jí)成了輕量鎖亏娜。
        //所以到這里,整個(gè)A類的對(duì)象焕窝,總共經(jīng)歷了40次的偏向撤銷
        A newA = new A();
        log.error("newA : {}",ClassLayout.parseInstance(a).toPrintable(a));
    }
輸出結(jié)果

直接就是001,無(wú)鎖不可偏向的標(biāo)識(shí)照藻。要知道袜啃,在我們這個(gè)程序中,前40個(gè)A類的對(duì)象被創(chuàng)建出來(lái)都是無(wú)鎖可偏向的狀態(tài)幸缕。而當(dāng)偏向撤銷達(dá)到了40次,JVM直接就將A類產(chǎn)生的對(duì)象改為了無(wú)鎖不可偏向的狀態(tài)

jvm源碼
//當(dāng)前線程是偏向鎖并且偏向自己
if(anticipated_bias_locking_value == 0) {
  
    success = true;
// 如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖晰韵,success不是true发乔,直接嘗試升級(jí)為輕量鎖
}else if(anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
              // try revoke bias
              // Class的對(duì)象頭(此時(shí)已經(jīng)被第40次改成禁用偏向了)
              markOop header = lockee->klass()->prototype_header();
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              // 如果鎖對(duì)象的markword沒有改變,cas將class對(duì)象的markword設(shè)置到鎖對(duì)象中
              if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
    
   
//  重偏向邏輯
}else if(anticipated_bias_locking_value & epoch_mask_in_place) !=0){
  ......
  success = true;
  
// 匿名偏向邏輯
else {
   ......
  success = true;
}

3. 輕量鎖

當(dāng)前jvm解釋_monitorenter指令時(shí), 當(dāng)偏向鎖的邏輯中,不是匿名偏向,不是偏向自己,不是可重偏向 或者 禁用偏向,都會(huì)升級(jí)為輕量鎖

jvm字節(jié)碼解釋器 bytecodeInterpreter.cpp對(duì)_monitorenter指令的解析片段 :

// 如果不是偏向鎖,或者升級(jí)偏向鎖失敗
if (!success) {
      // 創(chuàng)建一個(gè)無(wú)鎖狀態(tài)的markword
      markOop displaced = lockee->mark()->set_unlocked();
      // 設(shè)置到鎖記錄的lock屬性中
      entry->lock()->set_displaced_header(displaced);
      bool call_vm = UseHeavyMonitors;
      // cas 如果lockee鎖對(duì)象中的markword 和無(wú)鎖狀態(tài)的markword相同,把markword替換為lock_recrod的指針.升級(jí)為輕量鎖
      if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
        // Is it simple recursive case?
        if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
          entry->lock()->set_displaced_header(NULL);
        } else {
          // 否則重量鎖
          CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
        }
      }
    }
        // 執(zhí)行下一條指令
    UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);

4. 重量鎖

需要膨脹為重量鎖的話,說(shuō)明鎖的競(jìng)爭(zhēng)已經(jīng)到了非常激勵(lì)的地步,上面偏向鎖,輕量鎖的過程中,任何cas失敗,都會(huì)升級(jí)為重量鎖雪猪。

比如

  1. 匿名偏向,cas對(duì)象頭為當(dāng)前線程id失敗

  2. 重偏向cas替換當(dāng)前線程id 失敗

  3. 輕量鎖 cas對(duì)象頭失敗

重量鎖會(huì)創(chuàng)建Monitor對(duì)象,鎖對(duì)象markword前62位存儲(chǔ)的是關(guān)聯(lián)的Monitor對(duì)象的指針

Monitor對(duì)象
Monitor對(duì)象

Monitor對(duì)象設(shè)計(jì)的和aqs的源碼差不多栏尚。

EntryList

和AQS里的Node節(jié)點(diǎn)一樣,是一個(gè)等待隊(duì)列, 只不過順序喚醒的順序和AQS是相反的

  • AQS : 從隊(duì)列的頭部喚醒, 所以是先阻塞排隊(duì)的線程先被喚醒搶鎖。

  • Synchronized :是從EntryList 的尾部喚醒,后阻塞排隊(duì)的線程先喚醒搶鎖只恨。

waitSet

調(diào)用wait方法阻塞的線程隊(duì)列译仗,和aqs里的條件喚醒隊(duì)列差不多,只不過aqs里的支持多條件喚醒一部分進(jìn)去隊(duì)列等待加鎖,Monitor對(duì)象的waitSet則是喚醒全部等待的線程進(jìn)入EntryList。

Owner_thread

標(biāo)識(shí)當(dāng)前持有重量鎖的線程,aqs里也有, 可以用來(lái)判斷重量鎖重入官觅。

性能損耗最大

當(dāng)膨脹為重量鎖之后,對(duì)性能的損耗是最大的纵菌。因?yàn)樽枞枰{(diào)用到linux內(nèi)核的pthread_metux互斥量函數(shù)來(lái)阻塞線程, 自旋調(diào)用的linux內(nèi)核的spin_lock函數(shù) 等等。

都需要從用戶態(tài)切換到內(nèi)核態(tài)休涤,升級(jí)權(quán)限才能調(diào)用到os的函數(shù)咱圆。執(zhí)行完成之后,又要切換回用戶態(tài),從寄存器保存的線程狀態(tài)恢復(fù)過來(lái) 執(zhí)行下面的程序, 所謂cpu上下文切換。

moniter_enter整體源碼

lock_record對(duì)象

主要有_lock對(duì)象和一個(gè)_obj對(duì)象

class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  BasicLock _lock;                                    // the lock, must be double word aligned
  oop       _obj;                                     // object holds the lock;
  // 省略其他方法
}

jvm字節(jié)碼解釋器 bytecodeInterpreter.cpp對(duì)_monitorenter指令的解析:

CASE(_monitorenter): {
        // 獲取鎖對(duì)象
        oop lockee = STACK_OBJECT(-1);
        // derefing's lockee ought to provoke implicit null check
        CHECK_NULL(lockee);
        // find a free monitor or one already allocated for this object
        // if we find a matching object then we need a new monitor
        // since this is recursive enter
        // lockRecord 鎖記錄
        BasicObjectLock* limit = istate->monitor_base();
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
          if (most_recent->obj() == NULL) entry = most_recent;
          else if (most_recent->obj() == lockee) break;
          most_recent++;
        }
        if (entry != NULL) {
          // 將鎖對(duì)象設(shè)置到鎖記錄中
          entry->set_obj(lockee);
          int success = false;
          uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;

          markOop mark = lockee->mark();
          intptr_t hash = (intptr_t) markOopDesc::no_hash;
          // implies UseBiasedLocking
          // 如果沒有禁用偏向鎖
          if (mark->has_bias_pattern()) {
            uintptr_t thread_ident;
            uintptr_t anticipated_bias_locking_value;
            thread_ident = (uintptr_t)istate->thread();
            anticipated_bias_locking_value =
              (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
              ~((uintptr_t) markOopDesc::age_mask_in_place);

           // 當(dāng)前線程是偏向鎖并且偏向自己
            if  (anticipated_bias_locking_value == 0) {
              // already biased towards this thread, nothing to do
              if (PrintBiasedLockingStatistics) {
                (* BiasedLocking::biased_lock_entry_count_addr())++;
              }
              success = true;
            }
            // 如果,lockee類的對(duì)象作為偏向鎖被撤銷過40次以上,會(huì)在這里禁用偏向鎖功氨,success不是true
            else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
              // try revoke bias
              // class對(duì)象的對(duì)象頭(此時(shí)已經(jīng)被第40次改成禁用偏向了)
              markOop header = lockee->klass()->prototype_header();
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              // 將class對(duì)象的markword設(shè)置到鎖對(duì)象中,
              if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (*BiasedLocking::revoked_lock_entry_count_addr())++;
              }
            }
            // 偏向鎖過期,lockee類的對(duì)象作為偏向鎖,在被其他線程升級(jí)到輕量鎖的過程中,會(huì)先撤銷偏向鎖再升級(jí)為輕量鎖,
            // 如果這個(gè)過程超過20次,會(huì)將lockee類的鎖對(duì)象都設(shè)置為可重偏向(markword的線程id位設(shè)置為a線程的線程id)
            else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
              // try rebias
              // 創(chuàng)建偏向當(dāng)前線程的markword
              markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
              if (hash != markOopDesc::no_hash) {
                new_header = new_header->copy_set_hash(hash);
              }
              // cas 替換markword
              if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::rebiased_lock_entry_count_addr())++;
              }
              else {
                // cas失敗膨脹至重量鎖
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
            else {
              // try to bias towards thread in case object is anonymously biased
              // 匿名偏向
              // 先獲取之前的對(duì)象頭  00000000 101
              markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
                                                              epoch_mask_in_place));
              // hash一開始初始化為 沒有hash的狀態(tài)
              // 如果在升級(jí)偏向鎖的過程中,被其他線程生成了hashcode,那么之前上面這行代碼里獲取header就會(huì)有hash值,替換成無(wú)hash的狀態(tài),那么 在下面的cas過程中,無(wú)hash的header和內(nèi)存地址中有hash的header就會(huì)不一樣,就會(huì)cas失敗,升級(jí)為重量鎖
              if (hash != markOopDesc::no_hash) {
                header = header->copy_set_hash(hash);
              }
              // 新建一個(gè)對(duì)象頭,里面的線程id替換成當(dāng)前線程id
              markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
              // debugging hint
              DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
              // cas,如果之前的對(duì)象頭 和現(xiàn)在鎖對(duì)象的markword相同,則替換成功(并發(fā)情況下可能在上面的過程中被別的線程升級(jí)為偏向鎖了)
              if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
                if (PrintBiasedLockingStatistics)
                  (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
              }
              else {
                    // cas失敗,膨脹重量鎖
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
              success = true;
            }
          }

          // traditional lightweight locking
          if (!success) {
            // 創(chuàng)建一個(gè)無(wú)所狀態(tài)的markword
            markOop displaced = lockee->mark()->set_unlocked();
            // 設(shè)置到鎖記錄當(dāng)中
            entry->lock()->set_displaced_header(displaced);
            bool call_vm = UseHeavyMonitors;‘
            // cad 如果lockee鎖對(duì)象中的markword 和無(wú)鎖狀態(tài)的markword相同,把markword替換為entry的指針.升級(jí)為輕量鎖
            if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
              // Is it simple recursive case?
              if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
                entry->lock()->set_displaced_header(NULL);
              } else {
                // cas失敗,膨脹重量鎖
                CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
              }
            }
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
          istate->set_msg(more_monitors);
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末序苏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捷凄,更是在濱河造成了極大的恐慌忱详,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跺涤,死亡現(xiàn)場(chǎng)離奇詭異匈睁,居然都是意外死亡监透,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門软舌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)才漆,“玉大人,你說(shuō)我怎么就攤上這事佛点〈祭模” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵超营,是天一觀的道長(zhǎng)鸳玩。 經(jīng)常有香客問我,道長(zhǎng)演闭,這世上最難降的妖魔是什么不跟? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮米碰,結(jié)果婚禮上窝革,老公的妹妹穿的比我還像新娘。我一直安慰自己吕座,他們只是感情好虐译,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吴趴,像睡著了一般漆诽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锣枝,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天厢拭,我揣著相機(jī)與錄音,去河邊找鬼撇叁。 笑死供鸠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的税朴。 我是一名探鬼主播回季,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼正林!你這毒婦竟也來(lái)了泡一?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤觅廓,失蹤者是張志新(化名)和其女友劉穎鼻忠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帖蔓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年矮瘟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塑娇。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澈侠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埋酬,到底是詐尸還是另有隱情哨啃,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布写妥,位于F島的核電站拳球,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏珍特。R本人自食惡果不足惜祝峻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扎筒。 院中可真熱鬧莱找,春花似錦、人聲如沸嗜桌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)症脂。三九已至,卻和暖如春淫僻,著一層夾襖步出監(jiān)牢的瞬間诱篷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工雳灵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棕所,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓悯辙,卻偏偏與公主長(zhǎng)得像琳省,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躲撰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容