Java锁-synchronized底层原理

2022-05-05 16:12:00 浏览数 (1)

Java中的锁可以分为隐式锁和显示锁,Lock接口的锁都是显示锁。JVM内置锁就是隐式锁,synchronized就是隐式的锁。

显示锁:需要手动释放锁,可以设置是否为公平锁 隐式锁:不需要手动释放锁,非公平锁

Monitor

Lock接口实现的锁底层是通过AQS同步队列实现的。用到了unsafe.park()方法。synchronized 底层有一个monitor监视器,会监控持有锁的对象。如下图:

代码语言:javascript复制
monitorenter表示当前程序将进入同步块
monitorexit表示即将退出同步块,并且释放锁

那么JVM怎么知道我当前的对象是否已经加锁了呢。

代码语言:javascript复制
synchronized (object) {
    //代码逻辑
}

如上图,Monitor调用Enter方法进入监视区,它会监视object对象里面是否有锁标记,如果没有就给object加上锁标记,并执行后面的逻辑,最后释放锁,取消object的锁标记。Monitor.Exit退出监视区,并释放锁。

锁Monitor.Enter进入后,发现对象object上面已经有锁标记了,那么返回Monitor.Enter失败,并退出Monitor。之后再循环重试。

对象头

锁标记保存在对象头(方法区的类信息)中的Mark Word中。

锁升级

由于JDK的优化,synchronized锁有一个升级,极大的提升了锁的性能。

锁对象刚创建时,对象头里面是无锁的状态,当第一个线程进来时。锁升级为偏向锁;当第二个线程进来,升级为轻量级锁;第三个线程进来,等待抢锁,第四个,第五个线程进来也是等待争抢锁。争抢的线程变多了,锁就会升级为重量级锁。

无锁

无锁态时 Mark Word 标记位为01,是否偏向标记为0。

偏向锁

此时是否偏向标记为1。

轻量级锁

锁标志位

重量级锁

升级为重量级锁时,线程会有从用户态到内核态的切换,所以说,大量线程抢锁时,性能不是很好,建议使用Lock接口实现的锁。

注意,以上两种锁都是单机的锁,现在的系统都是分布式的,需要分布式锁。可以用zookeeper或redis实现。市面上已经有成熟的分布式锁框架。像Redisson,Curator等都很不错。

Lock 与synchronized

(1)synchronized不会导致死锁现象发生;而Lock可能造成死锁现象Lock可以让等待。 (2)锁的线程响应中断,而synchronized却不行。 (3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到 。 (4)Lock可以提高多个线程进行读操作的效率 。 (5)性能上,竞争不激烈两者差不多;非常激烈时(即有大量线程同时竞争) Lock远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

最后欢迎关注我的博客:https://lvshen9.gitee.io/

0 人点赞