【JavaSE】Lock锁,独占锁ReentrantLock的AQS源码,如何管理同步队列。acquire方法和release方法

本文详细解析了JavaSE中的Lock锁实现,尤其是独占锁ReentrantLock的AQS(AbstractQueuedSynchronizer)源码。文章介绍了获取和释放独占锁的流程,包括tryAcquire、addWaiter、acquireQueued、tryRelease方法的作用,以及线程如何在同步队列中进行入队和出队操作。通过对ReentrantLock源码的分析,展示了Lock锁的公平性和非公平性策略,并总结了独占锁的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Lock锁具体是如何实现的

由上一篇博客解释,实现Lock锁的子类,实现了接口的所有方法。每个方法又都依赖Sync这个内部静态类来实现的,所以主要看一下Sync这个内部静态类。

abstract static class Sync extends AbstractQueuedSynchronizer

Sync继承了AbstractQueuedSynchronizer这个抽象类,其实它是java语言中一个重要的队列同步器,简称AQS。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等)。
下面先来看一下AQS中和Lock锁有关的一些方法:
独占锁(ReentrantLock)本篇讲解独占锁

void acquire(int arg) //独占式获取同步状态,如果获取失败则插入同步队列进行等待。 
void acquireInterruptibly(int arg) //与acquire方法相同,但在同步队列中等待时可以响应中断。 
boolean tryAcquireNanos(int arg,long nanosTimeout) //在2的基础上增加了超时等待功能,在超时时间内没有获 得同步状态返回false 
boolean tryAcquire(int arg) //获取锁成功返回true,否则返回false 
boolean release(int arg) //释放同步状态,该方法会唤醒在同步队列中的下一个节点。

共享锁(ReentrantReadWriteLocK)

void acquireShared(int arg) //共享式获取同步状态,与独占锁的区别在于同一时刻有多个线程获取同步状态。 
void acquireSharedInterruptibly(int arg) //增加了响应中断的功能 
boolean tryAcquireSharedNanos(int arg,lone nanosTimeout) //在2的基础上增加了超时等待功能 
boolean releaseShared(int arg) //共享锁释放同步状态。

上面代码的解析中提到了一个同步队列,这个队列就是来管理那些同时竞争一个锁的时候,没有竞争到锁的线程,会进行排队放在一个数据结构中进行管理。那么这个数据结构是怎样的形式存在的?
在AQS有一个静态内部类Node,这是我们同步队列的每个具体节点。在这个类中有如下属性

volatile int waitStatus; // 节点状态 
volatile Node prev; // 当前节点的前驱节点 
volatile Node next; // 当前节点的后继节点 
volatile Thread thread; // 当前节点所包装的线程对象 
Node nextWaiter; // 等待队列中的下一个节点

可以初步推断出这些没有竞争到锁的线程,会被封装成一个节点,然后以双向链表的形式管理起来。
节点状态是由一个简单的int型来保存的,用来表示此线程目前所处的状态:

int INITIAL = 0; // 初始状态 
int CANCELLED = 1; // 当前节点从同步队列中取消 
int SIGNAL = -1; // 后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,使得后继 节点的线程继续运行。 
int CONDITION = -2; // 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了 signal()方法后,该节点将会从等待队列中转移到同步队列中,加入到对同步状态的获取中。 
int PROPAGATE = -3; // 表示下一次共享式同步状态获取将会无条件地被传播下去。

在队列中的管理,主要依靠这个int型的变量。
另外AQS中有两个重要的成员变量:

private transient volatile Node head; 
private transient volatile Node tail;

由此可知,整个双向链表是由头尾节点来管理的。这样更加方便线程的入队和出队操作。
那么,节点如何进行入队和出队操作?实际上这对应着锁的获取和释放两个操作:获取锁失败进行入队操作,获取 锁成功进行出队操作。

二、独占锁ReentrantLock

现在我们来看一下ReentrantLock中的一些加锁和解锁操作的源码是怎样的。

1.独占锁的获取

调用lock()方法是获取独占锁,获取失败就将当前线程加入同步队列,成功 则线程执行。来看ReentrantLock源码:

public void lock() {
   
        sync.lock();
    }

ReentrantLock的lock调用了sync的lock,我们看看sync是怎么来的:

//构造方法
public ReentrantLock() {
   
        sync = new NonfairSync();
    }
    
public ReentrantLock(boolean fair) {
   
        sync = fair ? new FairSync() : new NonfairSync();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值