1.文章目录
- AQS概述,类结构,源码导读;
- RenntractLock源码,实现细节导读;
2.AQS概述&类结构
AbstractQueuedSynchronizer 抽象同步队列简称 AQS ,它是实现同步器的基础组件, 并发包中锁的底层就是使用 AQS 实现的;
- AQS 是一个FIFO 的双向队列,其内部通过节点 head tail 首和队尾元素,队列元素的类型为Node 其中 Node 中的 thread 变量用来存放进入 AQS 队列里面的线程:
- Node 节点内部的 SHARED 用来标记该线程是获取共享资源资源时被阻 挂起后放入AQS 队列的, EXCLUSIVE 用来标记线程是获取独占资源时被挂起后放入 AQS 队列的,waitStatu 记录当前线程等待状态,可以为 CANCELLED (线程被取消了)、 SIGNAL 线程需要被唤醒)、 ONDITION (线程在条件队列里面等待〉、 PROPAGATE 释放共享资源时需要通知其他节点; prev 记录当前节点的前驱节点, next 记录当前节点的 后继节点
- AQS 维持了的状态信息 state,可以通过 getState setState compareAndSetState 函数修改其值,对于 ReentrantLock来说, state 可以用来表示前线程获取锁的可重入次数 ;对于 读写锁 ReentrantReadWri teLock 来说 state16 位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数; 对于 semaphore 来说 state 用来表示当前可用信号的 数:对于 CountDownlatch 来说,state C表示 数器当前的值;
- AQS 有个内部类ConditionObject 用来结合锁实现线程同步 ConditionObject 可以 直接访问 AQS 内部的变量,比如 state 态值和 AQS队列。ConditionObject 是条 变量 个条件变量对应 件队列 (单向链表队列),其用来存放调用条件变 await 方法后被阻塞的线程,如类图所示 这个条件队列的头、尾元素分别为firstWaiter,lastWaiter;
源码导读
基本属性:看上面的图可以直到基本属性:使用Unsafe机制,记录变量偏移量,方便CAS操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
acquire(int arg):获取独占资源方法
public final void acquire(int arg) {
// 首先调用tryAcquire尝试获取资源
if (!tryAcquire(arg) &&
,失败则(Node.EXCLUSIVE)状态进去等待队列,并且LockSupport.park挂起自己
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter方法
private Node addWaiter(Node mode) {
// 创建节点,记录当前线程
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
CAS插入
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果上面没有插入成功,则循环插入
enq(node);
return node;
}
private Node enq(final Node node) {
// 循环插入
for (;;) {
Node t = tail;
// 尾部为NULL,则head肯定也是NULl,创建比赋值
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// 设置tail节点
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
realease(int arg):释放资源
public final boolean release(int arg) {
// 调用tryRelease方法,主要是修改state的值
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 释放AQS队列一个被阻塞的线程
unparkSuccessor(h);
return true;
}
return false;
}
acquireShared(int arg)获取共享资源
// 首先尝试获取资源,如果不成功,LockSupport.park当前线程,封装Node.ShARED进入AQS队列
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
doRelaseShared:释放共享资源
// 尝试释放资源(设置state),如果成功则激活一个线程,判断是否满足自己的需要,不满足再次进去队列
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
- 到此基本的核心方法已经导读结束,AQS作为队列同步的抽象,对基本方法做了实现,tryAcquire,treRelease等方法要交给子类来实现,比如可重入锁,不可重入锁的设计肯定不同的;后续对ReentrantLock源码导读会更加清晰;
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
条件变量Condition
这个变量类似于wait,notify的作用,线程同步用的,比较基础;可以自己探索下;
3.ReentrantLock类结构&源码导读
- 可以看出,ReentrantLock实现了Lock接口,实现了基本接口,借助Sync(继承AQS)实现真正的线程同步策略;
- ReentrantLock 是可重入的独占锁只能有一个线程可获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面;
- 我们平常用这个的锁,有公平锁,非公平锁(默认)两种形式;因此内部封装了两种实现形式;
初始化方法:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 上述代码提供了两种初始化方式,也是我们最常用的;默认为非公平策略,也可以根据fair参数指定;
其他核心方法:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public int getHoldCount() {
return sync.getHoldCount();
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public boolean isLocked() {
return sync.isLocked();
}
// sync是否是FairSync的子类/类
public final boolean isFair() {
return sync instanceof FairSync;
}
protected Thread getOwner() {
return sync.getOwner();
}
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
public final int getQueueLength() {
return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
- 可以看出都是借助Sync(AQS)变量实现的;模板方法设计;
- 因此我们看Sync的实现
Sync类实现
- 该类继承AQS,对AQS的方法进行了重写,根据Reentrant策略进行设计;
- 具体的lock,tryRe
abstract static class Sync extends AbstractQueuedSynchronizer {
.........
// 交给fairSync,NoFairSync实现
abstract void lock();
}
final boolean nonfairTryAcquire(int acquires)
// 非公平策略尝试获取资源
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果c=0则表示没有锁占有
if (c == 0) {
// CAS设置state=acquires
if (compareAndSetState(0, acquires)) {
// 设置锁拥有线程
setExclusiveOwnerThread(current);
return true;
}
}
// 也是可重入锁的设计特点
// 锁被占有了,看下是不是自己
else if (current == getExclusiveOwnerThread()) {
// 是自己,对c进行加
int nextc = c + acquires;
// 超过最大数
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
tryRelease:释放资源
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果不是锁拥有线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果c=0了,释放资源完了
if (c == 0) {
// free=true,锁拥有线程为Null
free = true;
setExclusiveOwnerThread(null);
}
// CAS设置锁状态
setState(c);
return free;
}
isHeldExclusively
//判断锁是否被自己占有,判断食不知自己就好了
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
几个简单方法:
// 生产条件变量
final ConditionObject newCondition() {
return new ConditionObject();
}
// 获取锁Owner
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 获取锁持有数
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 是否被占有
final boolean isLocked() {
return getState() != 0;
}
- 到这几个基本方法介绍完了,我们看如何实现公平策略,非公平策略
FairSync&NoFairSync源码
NoFairSync类源码
- 继承Sync对lock,tryAcquire做了重写;
- 不公平体现在哪:第一个调用lock方法的线程就会获得,随机的
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// CAS设置state状态
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 失败,调用AQS的acquire,前面已经介绍,如果尝试获取失败后,进入阻塞队列
acquire(1);
}
// 服用父类Sync的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync类源码
- 继承Sync对lock,tryAcquire做了重写;
- 公平体现在:根据阻塞队列从前向后以此获得锁,根据调用lock的顺序进行获得锁;
- hasQueuedPredecessors实现了公平策略;
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 调用AQS上层方法,还是会掉tryAcquire方法
final void lock() {
acquire(1);
}
// 公平策略的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果锁释放了
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//加state
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
// 如果当前线程节点有前驱节点则返回住时, 如果当前AQS队列空或者当前线程节点是 AQS 第一个节
点则返回 false 。其中如果 h==t 说明当前队列,直接返回 false ;
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
4.总结
- 以上是AQS,ReentrantLock的基本实现原理,对于更多细节可以自己阅读;