一文带你了解ReentrantLock

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区别

  1. 两者都是可重入锁

    可重入锁:(递归锁)是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁**(前提,锁对象得是同一个对象)**,不会因为之前已经获取过还没释放而阻塞。

    一句话:一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。

  2. synchronized依赖于JVM 而ReentrantLock 依赖于API

    前者是JVM层面实现的,后者是JDK层面实现的,需要lock() 和unlock() 方法配合。

  3. 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();
      		}
      	}
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值