概述
ReentrantLock 是 Java 并发中十分常用的一个类,具备类似 synchronized 锁的作用。但是相比 synchronized, 它具备更强的能力,同时支持公平锁和非公平锁。
公平锁: 指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。
非公平锁: 多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队列的队尾等待。
那 ReentrantLock 中具体是怎么实现公平和非公锁的呢?它们之间又有什么优缺点呢?本文就带大家一探究竟。
RenentrantLock 原理概述

上面是 RenentrantLock 的类结构图。
-
RenentrantLock 实现了 Lock 接口,Lock 接口提供了锁的通用 api,比如加锁 lock,解锁 unlock 等操作。
-
RenentrantLock 底层加锁是通过 AQS 实现的,两个内部类 FairSync 服务于公平锁,NofaireSync 服务于非公平锁的实现,他们统一继承自 AQS。
ReentrantLock 类 API:
-
public void lock():获得锁
如果锁没有被另一个线程占用,则将锁定计数设置为 1
如果当前线程已经保持锁定,则保持计数增加 1
如果锁被另一个线程保持,则当前线程被禁用线程调度,并且在锁定已被获取之前处于休眠状态
-
public void unlock():尝试释放锁
如果当前线程是该锁的持有者,则保持计数递减
如果保持计数现在为零,则锁定被释放
如果当前线程不是该锁的持有者,则抛出异常
非公平锁实现
演示
@Testpublic void testUnfairLock() throws InterruptedException {// 无参构造函数,默认创建非公平锁模式ReentrantLock reentrantLock = new ReentrantLock();for (int i = 0; i < 10; i++) {final int threadNum = i;new Thread(() -> {reentrantLock.lock();try {System.out.println("线程" + threadNum + "获取锁");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {// finally中解锁reentrantLock.unlock();System.out.println("线程" + threadNum +"释放锁");}}).start();Thread.sleep(999);}Thread.sleep(100000);}
运行结果:
线程0获取锁线程0释放锁线程1获取锁线程1释放锁线程3获取锁线程3释放锁线程2获取锁线程2释放锁线程5获取锁线程5释放锁线程4获取锁线程4释放锁....
-
默认构造函数创建的是非公平锁
-
运行结果可以看到线程 3 优先于线程 2 获取锁(这个结果是人为造的,很难模拟出来)。
加锁原理
-
构造函数创建锁对象
public ReentrantLock() {sync = new NonfairSync();}
-
默认构造函数,创建了 NonfairSync,非公平锁同步器,它是继承自 AQS.
-
第一个线程加锁时,不存在竞争,如下图:
// ReentrantLock.NonfairSync#lockfinal void lock() {// 用 cas 尝试(仅尝试一次)将 state 从 0 改为 1, 如果成功表示【获得了独占锁】if (compareAndSetState(0, 1))// 设置当前线程为独占线程setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);//失败进入}

-
cas 修改 state 从 0 到 1,获取锁
-
设置锁对象的线程为当前线程
-
第二个线程申请加锁时,出现锁竞争,如下图:

-
Thread-1 执行,CAS 尝试将 state 由 0 改为 1,结果失败(第一次),进入 acquire 逻辑
// AbstractQueuedSynchronizer#acquirepublic final void acquire(int arg) {// tryAcquire 尝试获取锁失败时, 会调用 addWaiter 将当前线程封装成node入队,acquireQueued 阻塞当前线程,// acquireQueued 返回 true 表示挂起过程中线程被中断唤

本文详细解析了Java并发工具类ReentrantLock的公平锁和非公平锁的实现原理。公平锁确保按照线程申请锁的顺序获取锁,而非公平锁则允许线程尝试直接获取锁,提高效率但可能导致线程饥饿。ReentrantLock通过AQS(AbstractQueuedSynchronizer)来管理锁的状态,通过公平锁和非公平锁的内部类FairSync和NonfairSync实现不同的锁策略。文章通过代码分析展示了加锁和释放锁的具体过程。
最低0.47元/天 解锁文章
41万+

被折叠的 条评论
为什么被折叠?



