1 ReentrantLock
1.1 ReentrantLock 是什么?
ReentrantLock是⼀个非抽象类,它是Lock接⼝的JDK默认实现,实现了锁的基本功能。从名字上看,它是⼀个"可重⼊"锁,从源码上看,它内部有⼀个抽象类 Sync ,是继承了AQS,自己实现的⼀个同步器。同时,ReentrantLock内部有两个非抽象类 NonfairSync 和 FairSync ,它们都继承了Sync。从名字上看得出,分别是"非公平同步器"和"公平同步器"的意思。这意味着ReentrantLock可以⽀持"公平锁"和"非公平锁"。通过看着两个同步器的源码可以发现,它们的实现都是"独占"的。都调用了AQS的 setExclusiveOwnerThread
⽅法,所以ReentrantLock的锁的"独占"的,也就是说,它的锁都是"排他锁",不能共享。
在ReentrantLock的构造⽅法⾥,可以传⼊⼀个 boolean 类型的参数,来指定它是否是⼀个公平锁,默认情况下是⾮公平的。这个参数⼀旦实例化后就不能修改,只能通过 isFair() ⽅法来查看。
// 创建非公平锁构造器
public ReentrantLock() {
sync = new NonfairSync();
}
// 通过传入布尔值为true来创建公平锁的构造器
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁是指多个线程在等待同一个锁时,必须按照
申请锁的时间顺序
来依次获得锁(FIFO);而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁
。(可能发生长时间得不到锁的情况)所以要根据实际的需求来选择非公平锁和公平锁。
1.2 ReentrantLock 和 Synchronized区别
-
两者都是可重入锁
可重入锁:(递归锁)是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁**(前提,锁对象得是同一个对象)**,不会因为之前已经获取过还没释放而阻塞。
一句话:一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。
-
synchronized依赖于JVM 而ReentrantLock 依赖于API
前者是JVM层面实现的,后者是JDK层面实现的,需要lock() 和unlock() 方法配合。
-
ReentrantLock 比synchronized增加了一些高级功能
-
等待可中断
ReentrantLock提供了一种能够中断等待锁的线程的机制,通过
lock.lockInterruptibly()
来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。 -
公平锁
ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock
默认情况是非公平的
,可以通过 ReentrantLock类的ReentrantLock(boolean fair)
构造方法来制定是否是公平的。 -
锁绑定多个条件
是指ReentrantLock对象可以同时绑定多个Condition对象。在synchronized中,锁对象的wait()跟它的notify()或者notifyAll()方法配合可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外添加一个锁;而ReentrantLock 则无须这样做,多次调用newCondition()方法即可。
/** * 锁绑定多个条件Condition * 题目:多线程之间按顺序执行,实现A->B->C三个线程启动,要求如下: A打印5次,B打印10次,C打印15次, * 紧接着 A打印5次,B打印10次,C打印15次, . . . 循环执行10轮 */ public class LockConditionDemo { public static void main(String[] args) { ShareResource shareResource = new ShareResource(); new Thread(() -> { for (int i = 0; i < 10; i++) { shareResource.print5(); } } , "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { shareResource.print10(); } } , "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { shareResource.print15(); } } , "C").start(); } } /** * 共享资源类 */ class ShareResource { // A:1 B:2 C:30 private int num = 1; private Lock lock = new ReentrantLock(); private Condition conditionA = lock.newCondition(); private Condition conditionB = lock.newCondition(); private Condition conditionC = lock.newCondition(); // 循环打印5次 public void print5() { // 1、获取锁资源 lock.lock(); try { // 2、判断是否可以执行业务 while (num != 1) { // 阻塞等待 conditionA.await(); } // 模拟业务执行 for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "\t" + (i + 1)); } // 3、通知其他线程,通过signal()方法唤醒线程 num = 2; conditionB.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } // 循环打印10次 public void print10() { // 1、获取锁资源 lock.lock(); try { // 2、判断是否可以执行业务 while (num != 2) { conditionB.await(); } // 模拟业务执行 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "\t" + (i + 1)); } // 3、通知其他线程 num = 3; conditionC.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } // 循环打印15次 public void print15() { // 1、获取锁资源 lock.lock(); try { // 2、判断是否可以执行业务 while (num != 3) { conditionC.await(); } // 模拟业务执行 for (int i = 0; i < 15; i++) { System.out.println(Thread.currentThread().getName() + "\t" + (i + 1)); } // 3、通知其他线程 num = 1; conditionA.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
-