这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
定义:
全称AbstractQueuedSynchronizer,是阻塞式锁和相关同步器工具的框架。
特点
1、用state属性来表示资源的状态(独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
getState -- 获取state状态
setStatus -- 设置status状态
compareAndSetStatus -- cas机制设置state状态
独占模式式只有一个线程能访问资源,而共享模式可以允许多个线程访问资源
2、提供了基于FIFO的等待队列,类似于Monitor 的EntryList
3、条件变量来实现等待、唤醒机制,支持多个条件变量,类似Monitor的WaitSet
子类主要实现以下方法(默认抛出UnsupportedOperationException)
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively
自定义锁
/**
* @Author blackcat
* @version: 1.0
* @description:自定义锁测试
*/
@Slf4j
public class MyLockTest {
public static void main(String[] args) {
MyLock lock = new MyLock();
new Thread(() -> {
lock.lock();
try {
log.debug("locking...");
Sleeper.sleep(1);
} finally {
log.debug("unlocking...");
lock.unlock();
}
},"t1").start();
new Thread(() -> {
lock.lock();
try {
log.debug("locking...");
} finally {
log.debug("unlocking...");
lock.unlock();
}
},"t2").start();
}
}
/**
* 自定义锁(不可重入锁)
*/
class MyLock implements Lock {
private MySync sync = new MySync();
//独占锁
class MySync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0,1)){
//加上锁,设置owner为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
//释放锁,设置owner为当前线程
setExclusiveOwnerThread(null);
//只有自己释放,不需要用cas设置
//当前state是volatile,exclusiveOwnerThread 不是volatile
//把这个放在后面添加写屏障使得上面的设置其他线程可见
setState(0);
return true;
}
//是否持有独占锁
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
public Condition newCondition(){
return new ConditionObject();
}
}
//加锁 不成功放入等待队列
@Override
public void lock() {
sync.acquire(1);
}
//加锁可打断
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试加锁(尝试一次)
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
//尝试加锁(带超时时间)
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException{
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
//解锁,唤醒等待线程
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
Reentrantlock
非公平锁实现原理
加锁解锁流程
//构造器
public ReentrantLock() {
sync = new NonfairSync();
}
static final class NonfairSync{
final void lock() {
// 首先用 cas 尝试(仅尝试一次)将 state 从 0 改为 1, 如果成功表示获得了独占锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果尝试失败
acquire(1);
}
}
没有竞争时候
发生竞争时候
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// 当 tryAcquire 返回为 false 时, 先调用 addWaiter, 接着acquireQueued
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//获得锁以后重写打断
selfInterrupt();
}
整个这块代码里面包含了四个方法的调用,如下:
-
tryAcquire,分别由继承 AQS 的公平锁(FairSync)、非公平锁(NonfairSync)实现。
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } //Sync 继承过来的方法 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //如果还没有获取锁 if (c == 0) { //尝试用cas获得 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //如果以前获得了锁,线程还是当前线程,则表示发生了锁重入 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } //获取失败 return false; } -
addWaiter,该方法是 AQS 的私有方法,主要用途是方法 tryAcquire 返回 false 以后,也就是获取锁失败以后,把当前请求锁的线程添加到队列中,并返回 Node 节点。
private Node addWaiter(Node mode) { //将当前线程关联一个Node 对象上,模式为独占模式 Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure //如果tail 不为null,cas 尝试将Node 对象加入AQS队列尾部 Node pred = tail; if (pred != null) { //双向链表结构 node.prev = pred; //compareAndSetTail 不一定一定成功,因为在并发场景下,可能会出现操作失败。那么失败 //后,则需要调用 enq 方法,该方法会自旋操作,把节点入队列。 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //尝试将Node 加入AQS enq(node); return node; } //当队列为空时,则会新创建一个节点,把尾节点指向头节点,然后继续循环 //第二次循环时,则会把当前线程的节点添加到队尾 private Node enq(final Node node) { //自旋+CAS for (;;) { Node t = tail; if (t == null) { // Must initialize //还没有,设置head为哨兵节点 if (compareAndSetHead(new Node())) tail = head; } else { // cas 尝试将Node 对象放入AQS队列尾部 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } -
acquireQueued,负责把 addWaiter 返回的 Node 节点添加到队列结尾,并会执行获取锁操作以及判断是否把当前线程挂起。
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); //上一个节点时head,表示轮到自己,尝试获取 if (p == head && tryAcquire(arg)) { //如果获取成功,设置自己为head //node 的prev和线程变成 null setHead(node); //上一个节点help GC p.next = null; // help GC failed = false; //返回中断标记 return interrupted; } // 判断是否应当 park if (shouldParkAfterFailedAcquire(p, node) && // park 等待, 此时 Node 的状态被置为 Node.SIGNAL parkAndCheckInterrupt()) //如果是因为interrupt 被唤醒,返回打断标记为true interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 获取上一个节点的状态 int ws = pred.waitStatus; // 上一个节点都在阻塞, 那么自己也阻塞好了 if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; // > 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. */ // 这次还没有阻塞 // 但下次如果重试不成功, 则需要阻塞,这时需要设置上一个节点状态为 Node.SIGNAL compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } // 线程挂起等待被唤醒 private final boolean parkAndCheckInterrupt() { //如果打断标记已经是true,则park失效 LockSupport.park(this); //调用interrupted 清除打断标记 return Thread.interrupted(); } -
selfInterrupt,是 AQS 中的
Thread.currentThread().interrupt()方法调用,它的主要作用是在执行完 acquire 之前自己执行中断操作。
Thread-1 执行了
1.CAS 尝试将state 由0改为1,结果失败
2.进入tryAcquire逻辑,这时stste已是1,结果仍然失败
3.接下来进入addWaiter的逻辑,构造Node队列
图中黄色三角表示Node的waitStatus状态,其中0为默认正常状态
Node的创建时懒惰的
其中第一个Node时哑元(或哨兵),用来占位,并不用来关联线程
当前线程进入acquireQueued逻辑
1.acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
2.如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁(如果获取成功,设置自己为head)
3.如果再次获取锁失败, 进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1,这次返回 false
4.shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued
5.当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回true
6.进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)
再次有多个线程经历上述过程竞争失败,变成这个样子
当thread-0 释放解锁后,会唤醒下一个thread-1的节点
如果加锁成功(没有竞争),会设置
exclusiveOwnerThread 为 Thread-1,state = 1
head 指向刚刚 Thread-1 所在的 Node,该 Node 清空 Thread
原本的 head 因为从链表断开,而可被垃圾回收
如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了
如果不巧又被 Thread-4 占了先
Thread-4 被设置为 exclusiveOwnerThread,state = 1
Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞
解锁源码
// Sync 继承自 AQS
static final class NonfairSync extends Sync {
// 解锁实现
public void unlock() {
sync.release(1);
}
}
//从AQS继承
public final boolean release(int arg) {
//尝试释放锁
if (tryRelease(arg)) {
//队列头节点
Node h = head;
//队列不为null,状态为Node.SIGNAL
if (h != null && h.waitStatus != 0)
//unpark AQS中的等待线程
unparkSuccessor(h);
return true;
}
return false;
}
//Sync 继承
protected final boolean tryRelease(int releases) {
//state --
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 支持锁重入, 只有 state 减为 0, 才释放成功
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//AQS 继承
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.
*/
// 如果状态为 Node.SIGNAL 尝试重置状态为 0
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.
*/
Node s = node.next;
// 不考虑已取消的节点, 从 AQS 队列从后至前找到队列最前面需要 unpark 的节点
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);
}
公平锁实现原理
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
// AQS 继承过来的方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
// 与非公平锁主要区别在于 tryAcquire 方法的实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
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;
// h != t 时表示队列中有 Node
return h != t &&
// (s = h.next) == null 表示队列中还有没有老二
// 或者队列中老二线程不是此线程
((s = h.next) == null || s.thread != Thread.currentThread());
}
条件变量的原理
每个条件变量其实就对应着一个等待队列,其实现类是 ConditionObject
//AQS
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//添加一个 Node 至等待队列
Node node = addConditionWaiter();
//释放同步器上面的锁,唤醒下一个节点
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//阻塞当前线程
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
//将该Node 加入AQS 尾部
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}