JAVA------synchronized关键字

概述

synchronized关键字是通过对一个对象加锁,来解决多线程间的同步问题,具体使用不做赘述,主要讨论synchronized的实现原理。

详情

先看段代码:

public class T09_Sync {
    public static void main(String[] args) {
        Object o=new Object();
        synchronized (o){
            System.out.println("Hello");
        }
    }
}

代码很简单,只是在执行输出前加上锁,运行javap -v命令看下生成的字节码文件

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #2                  // class java/lang/Object
         3: dup
         4: invokespecial #1                  // Method java/lang/Object."<init>":()V
         7: astore_1
         8: aload_1
         9: dup
        10: astore_2
        11: monitorenter
        12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        15: ldc           #4                  // String Hello
        17: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        20: aload_2
        21: monitorexit
        22: goto          30
        25: astore_3
        26: aload_2
        27: monitorexit
        28: aload_3
        29: athrow
        30: return

通过字节码文件我们能看到,在输出前后有这么两句指令monitorentermonitorexit,那么这两个指令有何含义呢?
查询jvm文档monitorentermonitorexit含义如下:
monitorenter

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

  • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
  • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
  • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

大致翻译过来:

每个对象都要与一个monitor相关联,只有当monitor被占有时才会被锁定,线程执行monitorenter命令尝试获取monitor的所有权,过程如下:

  • 如果monitor的重入数为0,则当前线程获取锁,并把monitor的重入数置为1,当前线程为monitor的所有者;
  • 如果当前线程已经拥有此monitor,则将monitor的重入数加1;
  • 如果已经有另外的线程占有此monitor,则当前线程阻塞(block),直到monitor的重入数为0,再次尝试获取monitor的所有权;

monitorexit

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

大致翻译过来:

执行monitorexit命令的线程必须是monitor的所有者
当前线程减少monitor的重入数目(-1),若重入数结果为0,则当前线程退出monitor的占有,其他阻塞的线程将被允许尝试获取monitor的所有权。

总结下来就是通过monitorenter和monitorexit命令实现。

在早期的锁依赖于系统的同步函数,在linux上使用mutex互斥锁,然而这又涉及到用户态和内核态切换的过程,即需要向操作系统申请“锁”资源,成本较高。然而在jkd1.6后,新增了偏向锁,和轻量级锁,它们的引入是为了解决在没有多线程竞争或基本没有竞争的场景下因使用传统锁机制带来的性能开销问题。

在讨论轻量级锁和偏向锁前,先看下对象的的构成,这与锁息息相关,毕竟在java中任意对象都能作为锁。
在这里插入图片描述

上图我们可以看到,对对象头中有个markword占有八个字节,如果要存有锁信息的话也只能是这了,以下是32位机器中markword的状态详情:
在这里插入图片描述
通过上图我们可以看到,markword中总共可以记录四种锁状态(无锁,轻量级锁,偏向锁锁,重量级锁)和GC标记,那么这几种锁的实际含义以及他们的转换顺序是什么呢?

  • 无锁状态:没有加锁;
  • 偏向锁:当前只有一个线程尝试获取锁,没有多余的线程和他竞争,此时只是在锁对象的markwork中填入当前线程的线程id,表示当前锁已被该线程获得;
  • 轻量级锁:当前线程CAS方式获取锁,直到成功;
  • 重量级锁:传统的锁,需要向操作系统申请,需要内核态和用户态的切换;

锁升级顺序如下:
无锁 - 偏向锁 -轻量级锁(自旋锁)-重量级锁

当只有一个线程获取锁时,由无锁升级为偏向锁 , markword 上记录当前线程指针,下次同一个线程加锁的时候,不需要争用,只需要判断线程指针是否同一个,所以,偏向锁,偏向加锁的第一个线程 。hashCode备份在线程栈上 线程销毁,锁降级为无锁;若有线程竞争, 锁升级为轻量级锁 ,每个线程有自己的LockRecord在自己的线程栈上,用CAS去争用markword的LR的指针,指针指向哪个线程的LR,哪个线程就拥有锁自旋超过一定次数,升级为重量级锁 , 如果太多线程自旋 CPU消耗过大,不如升级为重量级锁,进入等待队列(不消耗CPU);

总结

通过上面的介绍可以看出,synchronized的底层基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值