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

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

 

隐式锁:不需要手动释放锁,非公平锁


Monitor

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

Java锁-synchronized底层原理_java


       

monitorenter表示当前程序将进入同步块monitorexit表示即将退出同步块,并且释放锁

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

synchronized (object) {    //代码逻辑}

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

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


对象头

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

Java锁-synchronized底层原理_java_02


锁升级

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


Java锁-synchronized底层原理_java_03

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


无锁


Java锁-synchronized底层原理_java_04

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


偏向锁


Java锁-synchronized底层原理_java_05

此时是否偏向标记为1。


轻量级锁


Java锁-synchronized底层原理_java_06


锁标志位


Java锁-synchronized底层原理_java_07


重量级锁


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

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


Lock 与synchronized


(1)synchronized不会导致死锁现象发生;而Lock可能造成死锁现象Lock可以让等待。

(2)锁的线程响应中断,而synchronized却不行。 

(3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到 。

(4)Lock可以提高多个线程进行读操作的效率 。

(5)性能上,竞争不激烈两者差不多;非常激烈时(即有大量线程同时竞争)                  Lock远远优于synchronized。所以说,在具体使用时要根据适当情况选择。