并发编程:synchronized

预备知识https://blog.csdn.net/qq_40977118/article/details/108291922

1. 同步代码块

package org.example.test;

import org.example.entity.A;

public class SynchronizedTest {

    public static void main(String[] args) {
        A a = new A();
        int i = 0;
        synchronized (a){
            i++;
        }
        System.out.println(i);
    }

}

2. 反编译

javap -c SynchronizedTest.class

在这里插入图片描述
关于monitor方法的源码http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/interpreter/bytecodeInterpreter.cpp
在这里插入图片描述

  • 代码在进入synchronized之前,a对象的对象头的锁状态应该是无锁可偏向(101)
  • 当jvm遇到synchronized关键字时,会在当前线程栈中产生一个lock record对象,由object reference和_displaced_header两部分组成,object reference指向锁对象a,_displaced_header是空
    在这里插入图片描述
  • 进入到synchronized(a)代码块内以后,_displaced_header会保存a对象的mark word(61个0+101),结束后锁对象的mark word会变为A线程id+epoch+age+101

在这里插入图片描述

3. 偏向锁(A线程来了)

  1. A线程来了,第一次拿锁,判断mark word是否可偏向,在第一次拿锁不计算hashcode的情况下,一定是可偏向的;
  2. 判断是不是自己, 如果不是自己,判断是不是匿名偏向,第一加锁肯定是匿名的(锁没有指向任何一个线程的id),经过一次cas操作,得到一个偏向自己的偏向锁;
  3. 如果是自己,而且没有过期,得到偏向锁,后面同一个线程再次加锁的效率非常高,只需要判断对象头内的线程id是不是相同,偏向时间戳是不是过期即可,如果线程id相同,时间戳也没有过期,直接拿锁,少了一次cas操作。

在这里插入图片描述

  • 如果A线程再次拿锁,还是偏向锁;注意重新加锁和重入不一样
  • 重新加锁和重入的区别:重新加锁是释放之后再次加锁;重入是没有释放之后再次加锁
sync(a){//1 
	xxxxxx 
}
sync(a){//2 
	zzzzz 
}
//上面的叫做重新加锁;如果是1的时候是偏向;那么2也是偏向 
sync(a){//1 
	xxxx 
	sync(a){//2 
	zzzz 
} 
}
//上面的代码叫做重入
  • A线程代码执行结束后,锁对象的对象头内的锁状态仍然是偏向锁(101),只是前54位带了线程id
    在这里插入图片描述
package org.example.test;

import org.example.entity.A;
import org.openjdk.jol.info.ClassLayout;

public class NoLockTest {
    static A a = new A();
    static Thread t1;
    public static void main(String[] args) throws InterruptedException {
        t1 = new Thread(){
            @Override
            public void run() {
                synchronized (a){
                    System.out.println("name:"+Thread.currentThread().getName());
                    System.out.println(ClassLayout.parseInstance(a).toPrintable());
                }
            }
        };  
        t1.setName("t1");
        t1.start();
        t1.join();
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }

}

在这里插入图片描述

  • 偏向锁详细处理流程

在这里插入图片描述

4. 轻量锁(B线程来了)

  • 接着偏向锁的流程,锁已经被A线程持有,所以锁的对象头内线程id一定不是B线程id,而且又不是匿名偏向的,就会进入轻量锁加锁逻辑
  • 当B线程尝试拿锁时,先把lock record的object reference指向偏向A线程的锁(101),然后设置一个无锁(001)的mark word,再将这个mark word存入_displaced_header

在这里插入图片描述

  • 执行cas,这时只有锁状态为无锁(001)时,才会cas成功,接着将锁对象mark word(64位)变为指向lock record的指针(62位)+轻量锁(00),不会发生锁膨胀,就不会升级为重量锁;

在这里插入图片描述

  • cas判断当前锁状态是不是无锁,如果是无锁,就把当前锁mark word存入lock record的_displaced_header,再把指向lock record的指针放入mark word中,同时变为轻量锁
  • 轻量锁在退出同步代码块时,会把锁的状态置为无锁(001),lock record存入mark word是为了在释放锁时,恢复锁对象的mark word。

在这里插入图片描述

  • 关闭偏向延迟-XX:BiasedLockingStartupDelay=0
package org.example.test;

import org.example.entity.A;
import org.openjdk.jol.info.ClassLayout;

public class NoLockTest {
    static A a = new A();
    static Thread t1;
    static Thread t2;

    public static void main(String[] args) throws InterruptedException {
        t1 = new Thread(){
            @Override
            public void run() {
                synchronized (a){
                    System.out.println("name:"+Thread.currentThread().getName());
                    System.out.println(ClassLayout.parseInstance(a).toPrintable());
                }
                t2.setName("t2");
                t2.start();
            }
        };
        t2 = new Thread(){
            @Override
            public void run() {
                synchronized (a) {
                    System.out.println("name:"+Thread.currentThread().getName());
                    System.out.println(ClassLayout.parseInstance(a).toPrintable());
                }
            }
        };
        t1.setName("t1");
        t1.start();
        t1.join();
        t2.join();
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }

}
name:t1
org.example.entity.A object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           05 10 c2 1e (00000101 00010000 11000010 00011110) (516034565)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
     12     4    int A.i                                       0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

name:t2
org.example.entity.A object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           40 f3 88 1f (01000000 11110011 10001000 00011111) (529068864)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
     12     4    int A.i                                       0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

org.example.entity.A object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
     12     4    int A.i                                       0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

5. 重量锁(C线程来了)

cas成功

  • B线程代码执行结束,C线程来了(属于交替执行),只有锁状态为无锁(001)时,cas成功,将锁变为轻量锁,不会发生锁膨胀,就不会升级为重量锁;

cas失败

  • 如果B线程代码还没执行结束,在准备cas之前,被cpu切换成执行C线程,C执行cas,并且成功了,把mark word变成001,并放入lock record的_displaced_header;mark word改成指向C线程lock record的指针,并且锁状态是轻量锁(00);这时候,cpu时间片再次切换到B线程,B接着原来的代码继续走,开始执行cas,这时候cas必定失败,因为object lock锁对象已经被C线程改成轻量锁(00),此时锁膨胀为重量锁。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值