谈谈 synchronized 和 ReentrantLock 的区别

① 两者都是可重入锁

“可重入锁”概念是:自己可以再次获取自己的内部锁。比如,一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁时,还可以再获取的;如果不可锁重入的话,就会造成死锁;同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时,才能最终释放锁。

② synchronized 依赖于 JVM,而 ReentrantLock 依赖于 API

synchronized 是依赖于 JVM 实现的,Java 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多的优化,但这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),可以通过查看它的源代码,来看它是如何实现的。

③ ReentrantLock 比 synchronized 增加了一些高级功能

相比 synchronized,ReentrantLock 增加了一些高级功能。主要有三点:① 等待可中断;② 可实现公平锁;③ 可实现选择性通知(锁可以绑定多个条件):

  • ReentrantLock 提供了一种能够中断等待锁线程的机制,通过 lock.lockInterruptibly() 来实现这个机制,也就是说正在等待的线程可以选择放弃等待,改为处理其他事情;
  • ReentrantLock 可以指定是公平锁还是非公平,而 synchronized 只能是非公平锁。所谓的公平锁,就是先等待的线程最先获得锁;ReentrantLock 默认是非公平的,可以通过 ReentrantLock 类的 ReentrantLock(boolean fair) 构造方法来制定是否是公平的;
  • synchronized 关键字结合 wait()notify()/notifyAll() 方法使用,可以实现等待/通知机制,ReentrantLock 类则需要借助于 Condition 接口与 newCondition() 方法。Condition 是 JDK1.5 之后才有的,它具有很好的灵活性,比如可以实现多路通知功能,也就是在一个 Lock 对象中可以创建多个 Condition 实例(即对象监视器),线程对象可以注册在指定的 Condition 中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用 notify()/notifyAll() 方法进行通知时,被通知的线程是由 JVM 选择的。而 synchronized 关键字就相当于整个 Lock 对象中只有一个 Condition 实例,所有的线程都注册在它一个身上。如果执行 notifyAll() 方法的话,就会通知所有处于等待状态的线程,这样会造成很大的效率问题,而 Condition 实例的 signalAll() 方法只会唤醒注册在该 Condition 实例中的所有等待线程。

你可能感兴趣的:(谈谈 synchronized 和 ReentrantLock 的区别)