「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战」
AQS的最核心的三个变量:state, 等待队列 堵塞队列,通过这三个核心变量,来进行对资源的管控。
aqs核心结构如下图:
说明:
condition队列我下面都写成了堵塞队列或者condtion队列
lock 需要排队获取锁的队列我下面写成了:等待队列
因为语义上有冲突,只要区分这两个队列即可。区别就是一个是双向列表,一个是单向列表。
ReentrantLock
Lock
ReentrantLock 分为两种模式:公平与非公平两种模式,它俩的区别在与抢锁的过程不太相同。
具体流程如下:
获取锁的流程
ReentrantLock lock = new ReentrantLock();
lock.lock()//获取锁的流程
复制代码
非公平锁
路径:java.util.concurrent.locks.ReentrantLock.NonfairSync#lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else acquire(1);
}
复制代码
解释:当非公平锁第一次进入会先进行一次cas设置state 这个状态值,如果设置成功,那么就获取到了锁,如果失败就走获取锁的流程。
公平锁
路径: java.util.concurrent.locks.ReentrantLock.FairSync#lock
final void lock() {
acquire(1);
}
复制代码
解释:我们发现公平锁并没有一上来就就去cas,而是很正常的走acquire() 获取锁的流程。
acquire
public final void acquire(int arg) {
// 第一次获取锁失败后,且加入到了等待队列中,并且由aqs管理,如果在等待队列中 堵塞状态下发生了中断,返回true if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
//中断线程设置中断位
}
1、第一步首先去枪锁,如果成功了直接退出,如果失败第二步
2、获取锁失败后的线程肯定要干嘛? 交给aqs去管理去排队,那么给aqs管理肯定先要创建节点,然后将这个节点在交给aqs管理,这个思想是比较重要的
addWaiter(Node.EXCLUSIVE) 就是创建节点
tryAcquire() 就是将新创建的节点,aqs进行管理
复制代码
下面的代码就是按照acquire() 流程去讲解。
非公平锁/公平锁 的 tryAcquire()
// 非公平锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); // 获取状态
if (c == 0) { // 如果为0 去抢一下
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); return true;
}
} //state 不为0 锁未被释放且 是线程的拥有者 state+1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; setState(nextc);
return true;
}
return false;
}
//公平锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//判断队列中有没有相关线程的节点已经在排队了。有则返回true表示线程需要排队,没有则返回false则表示线程无需排队。
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
}
复制代码
总结:
先获取state的值,如果是0说明当前没有线程获取锁,直接通过cas的方式去设置state,如果成功说明当前线程获取到锁。 如果当前获取锁的线程就是自己,那么直接是重入锁,+1. 否则获取锁失败 区别: 在state==0的时候,非公平会直接去抢锁,而公平锁会先去查看等待队列里面有节点,如果有等待获取锁的线程,则直接就去后面排队,不参与枪锁。
上面获取锁失败后,就会进入addWork()流程,该流程就是为了将获取锁冲突的线程进行保存,存放在等待队列中,等待前面节点的唤醒。




近期评论