【源码解析】从ReentrantLock角度聊聊AQS原理
2023-12-15 04:53:55
AQS结构
//头节点 当前持有锁的线程
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
//每个进来的线程都插入到最后
private transient volatile Node tail;
/**
* The synchronization state.
*/
//代表当前锁的状态 0:表示没有占用 大于0代表有线程持有当前锁
private volatile int state;
//CAS设置值
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
AQS内部包装成一个Node节点 通过state标识线程的状态
//等待线程被包装成一个Node节点
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
//共享模式下
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
//独享模式下
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
//线程取消争抢这个锁
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
//当前node的后继节点需要被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
static final int PROPAGATE = -3;
//强半天获取不到锁,就取消等待
volatile int waitStatus;
//前驱节点的引用
volatile Node prev;
//后继节点的引用
volatile Node next;
//本线程
volatile Thread thread;
// 这个是在condition中用来构建单向链表
Node nextWaiter;
}
// 使用static,这样每个线程拿到的是同一把锁
private static ReentrantLock reentrantLock = new ReentrantLock(true);
public void createOrder() {
// 比如我们同一时间,只允许一个线程创建订单
reentrantLock.lock();
// 通常,lock 之后紧跟着 try 语句
try {
// 这块代码同一时间只能有一个线程进来(获取到锁的线程),
// 其他的线程在lock()方法上阻塞,等待获取到锁,再进来
// 执行代码...
} finally {
// 释放锁
// 释放锁必须要在finally里,确保锁一定会被释放,如果写在try里面,发生异常,则有可能不会执行,就会发生死锁
reentrantLock.unlock();
}
}
Lock接口
public interface Lock {
//加锁
void lock();
//尝试获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁
void unlock();
Condition newCondition();
}
public class ReentrantLock implements Lock, java.io.Serializable
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
通过构造参数进行区分使用公平锁还是非公平锁。默认是非公平锁。
//继承AQS 正在的获取和释放锁是由Sync的实现类来控制的。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {
//xxxxxx
}
protected final boolean tryRelease(int releases) {
//xxxx
}
}
Lock 加锁
//sync进行管理锁
//公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//争抢锁🔒
final void lock() {
acquire(1);
}
}
父类实现的acquire
//lock.lock()
public final void acquire(int arg) {
//tryAcquire(arg) true 获取锁成功直接结束
//如果没有获取到锁,acquireQueued 会将线程压入队列中
//!tryAcquire(arg) 没有获取到锁,将当前线程挂起
//addWaiter
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//因为FairSync实现了
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();
// c == 0 当前没有线程获取锁
if (c == 0) {
//当前是公平锁,先来后到
//查看队列中是否有等待的线程
//hasQueuedPredecessors 没有的话才可以获取线程
//compareAndSetState CAS设置 有可能同时多个线程竞争锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//表示获取到锁了,标记以下
setExclusiveOwnerThread(current);
//执行到这里 表示获取锁成功
return true;
}
}
//当前有线程持有锁,先判断下 获取线程的锁是不是自己 也就是重入了
//state += 1;
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
//check
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//到这里表示没有获取到锁
//1.尝试获取失败
//2.也不是自己的可冲入锁
return false;
}
//队列中有等待的线程 返回true
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
//CAS设置值
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
protected final void setExclusiveOwnerThread(Thread thread) {
//将当前线程设置为获取到线程
//当前拥有锁的线程
exclusiveOwnerThread = thread;
}
//此方法的作用是将线程包装成node, 同时进入队列中
//EXCLUSIVE是独占模式
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;
}
//因为是公平锁,所以会按照链表的形式将当前任务添加到队列中
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取node的prev节点
final Node p = node.predecessor();
// p == head 说明当前节点虽然进到了阻塞队列,但是是阻塞队列的第一个,因为它的前驱是head
if (p == head && tryAcquire(arg)) {
// //到这里说明刚加入到等待队列里面的node只有一个,并且此时获取锁成功,设置head为node
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// // 到这里,说明上面的if分支没有成功,要么当前node本来就不是队头,
// // 要么就是tryAcquire(arg)没有抢赢别人,继续往下看
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//当前线程没有抢到锁 是否需要挂起当前线程
//第一个参数是前驱节点 第二个节点是当前线程的节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//前驱节点正常 -1 当前线程需要挂起
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// 136 // 前驱节点 waitStatus大于0 ,之前说过,大于0 说明前驱节点取消了排队。这里需要知道这点:
// 137 // 进入阻塞队列排队的线程会被挂起,而唤醒的操作是由前驱节点完成的。
// 138 // 所以下面这块代码说的是将当前节点的prev指向waitStatus<=0的节点,
// 139 // 简单说,如果前驱节点取消了排队,
// 140 // 找前驱节点的前驱节,往前循环总能找到一个waitStatus<=0的节点
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// // 仔细想想,如果进入到这个分支意味着什么
// 157 // 前驱节点的waitStatus不等于-1和1,那也就是只可能是0,-2,-3
// 158 // 在我们前面的源码中,都没有看到有设置waitStatus的,所以每个新的node入队时,waitStatu都是0
// 159 // 用CAS将前驱节点的waitStatus设置为Node.SIGNAL(也就是-1)
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
Lock 解锁
线程如果没有获取到锁,那么会挂起,LockSupport.park(this); 等待唤醒。
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
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;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final void setExclusiveOwnerThread(Thread thread) {
//将当前线程设置为获取到线程
//当前拥有锁的线程
exclusiveOwnerThread = thread;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// 唤醒后继节点,但是有可能后继节点取消了等待
// 从队尾往前找,找到waitStatus <= 0 的所有节点排在最前面的一个
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//唤醒线程
LockSupport.unpark(s.thread);
}
这里存在并发问题:从前往后寻找不一定能找到刚刚加入队列的后继节点。
如果此时正有一个线程加入等待队列的尾部,执行到上面第7行,第7行还未执行,解锁操作如果从前面开始找 头节点后面的第一个节点状态为-1的节点,此时是找不到这个新加入的节点的,因为尾节点的next 还未指向新加入的node,但是从后面开始遍历的话,那就不存在这种情况。
小结
锁状态,通过state标记, 0 没有线程占用锁,state >= 代表有线程获取到锁,> 1说明是可冲入锁。 所以lock和unlock必须是配对的。
线程的阻塞和解决阻塞:lockSupport.park(thread)挂起线程,unpack()唤醒线程。
阻塞队列: 争抢的线程很多,所以需要将等待的线程通过一个queue进行管理这些线程。AQS是一个FIFO的队列,
文章来源:https://blog.csdn.net/jia970426/article/details/134937916
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!