从零学习synchronized关键字

摘要

synchronized是java的关键字,也可以叫同步锁,能够保证在同一时刻最多只有一个线程执行同步代码代码块,以达到保证并发安全的效果。

性质

可重入性:当一个线程获取锁后,没有释放当前锁,再次获取当前锁时,可以直接进去,不需要再次获取锁,synchronized可以是一个悲观锁,独占锁。

原子性:确保同一时间只有一个线程能拿到锁,能够进入代码块。

不可中断性:一个线程获取锁之后,另外一个线程处于阻塞或者等待状态,前一个不释放,后一个也一直会阻塞或者等待,不可以被中断。

synchronized的三种使用场景

1.作用于普通方法上,表示this锁

public synchronized void run() {}

2.作用于静态方法上,表示锁的是class字节码

public synchronized static void run() {}

3.还可以自定义锁对象

 Object o =  new Object();
    public   void add() {
        synchronized (o){
            //处理任务逻辑
        }
    }

synchronized的底层原理

测试类如下:

public class Test003 {

    public synchronized void add() {
        synchronized (new Object()) {
        }
    }
}

通过编译后,我们进入class文件目录查看class文件,javap -p -v -c Test003.class 这里提供一部分

public Thread.Test003();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
        stack=1, locals=1, args_size=1
        0: aload_0
        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
        4: return
            LineNumberTable:
        line 7: 0
        LocalVariableTable:
        Start  Length  Slot  Name   Signature
        0       5     0  this   LThread/Test003;

        public void add();
        descriptor: ()V
        flags: ACC_PUBLIC ACC_SYNCHRONIZED   //标志位
        Code:
        stack=2, locals=3, args_size=1
        0: new           #2                  // class java/lang/Object
        3: dup
        4: invokespecial #1                  // Method java/lang/Object."<init>":()V
        7: dup
        8: astore_1
        9: monitorenter              //进去该方法,白话获取锁
        10: aload_1
        11: monitorexit             //执行结束,正常退出
        12: goto          20
        15: astore_2
        16: aload_1
        17: monitorexit            //发生异常,异常捕获退出
        18: aload_2
        19: athrow
        20: return

我们可以看到反编译后,由monitor对象来控制,
当我们进入方法的时候,执行monitorenter,就会获取当前对象的所有权,这个时候monitor进入数为1,当前的这个线程就是这个monitor的owner。
如果你已经是这个monitor的owner了,你再次进入,就会把进入数+1,当他执行完monitorexit,对应的进入数就-1,直到为0,才可以被其他线程持有,

标志位ACC_SYNCHRONIZED的作用

同步方法的时候,一旦执行到这个方法,就会先判断是否有标志位,然后ACC_SYNCHRONIZED会去隐式调用两个指令:monitorenter和monitorexit

为什么有两个monitorexit

第一个是正常结束退出,第二个是异常捕获退出,都会释放当前锁。

锁的升级

了解锁的升级过程先了解下对象的组成,对象一般由对象头,实例数据,填充数据组成,对象头由Mark Word,对象指针,数组长度组成。
Mark Word的构成:64位操作系统
从零学习synchronized关键字
锁的升级流程:
当线程获取锁的时候,会首先判断当前线程是否是同一个线程,如果是当前线程的话,会判断当前线程是否获取过锁,如果没有获取到标志位修改为,获取到标志位+1。在对象头中的markwork中是否持有偏向锁也更新为1,也就是我们所说的偏向锁。如果不是当前线程的话,升级为轻量级锁。jvm会在当前线程的栈帧中建立一个叫锁记录的空间,用来存储锁对象的Mark Word 拷贝,然后把Lock Record中的owner指向当前对象,通过cas操作如果修改成功获取锁成功,修改失败的话会短暂尝试一段时间,如果还是获取锁失败的话,继续升级为重量级锁。
从零学习synchronized关键字
**

synchronized关键字的知识就总结这么多,如有错误请留言指出,谢谢!!!

**

上一篇:JAVA多线程和并发


下一篇:多线程-JAVA 应用