一、ReentrantLock
重入锁ReentrantLock,就是支持重进入的锁,它表示该锁能够支持一个线程对资源重复加锁。ReentrantLock虽然不像synchronized关键字支持隐式的重进入,调用Lock方法时,获取到锁的线程仍然能再次调用Lock获取锁而不被堵塞。它提供了与synchronized关键字相似的同步,只是在使用时需要显式的获取和释放锁,缺少了便捷性,但拥有了可操作性,可中断获取锁,超时获取锁等特性,该锁还支持获取锁的公平和非公平选择(之后讲解)
(1)ReentrantLock实现了父接口Lock
public class ReentrantLock implements Lock, java.io.Serializable {
ReentrantLock是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成,这些方法就来源于父接口Lock,而synchronized是JVM的关键字。
三个主要方法:
public void lock() {
sync.lock();
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public void unlock() {
sync.release(1);
}
lock()方法是获取锁,tryLock()是尝试非阻塞的获取锁,unlock()是释放锁,至于为什么调用sync的方法,之后讲解
三个主要功能:
由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:
1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
公平锁、非公平锁的创建方式:
//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);
//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);
3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
(2)ReentrantLock底层实现
ReentrantLock是使用组合自定义同步器(AQS)来实现锁的获取和释放,下面就是ReentrantLock的AQS源码
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {//继承了AQS类
所以上面ReentrantLock的三个主要方法都是调用的AQS其中的方法
(3)AQS是什么?
队列同步器AbstractQueuedSynchronizer(简称同步器),是用来构建锁或其他同步组件的基础框架,通过内置的FIFO队列来完成获取线程的排队工作,AQS主要利用硬件原语指令(CAS compare-and-swap),来实现轻量级多线程同步机制,并且不会引起CPU上文切换和调度,同时提供内存可见性和原子化更新保证(线程安全的三要素:原子性、可见性、顺序性)。
二、两者的区别
(1)synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的
(2)synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
(3)synchronized比较简单,ReentrantLock需要lock()和unlock()来实现,较为复杂