1. AQS源码阅读
1.1 AQS简介
AbstractQueuedSynchronizer
简称AQS,是实现JUC包中各种锁的关键,此类是一个模板类,具体的ReentrantLock
、CountDownLatch
、ReadWriteLock
等等都是自己去实现里边变量的使用规则。
各种类型的锁都有自己的锁类型信息
-
比如
ReadWriteLock
就肯定会有当前的锁状态是读锁模式还是写锁模式static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式 static final Node EXCLUSIVE = null; // 当前锁状态是 排它锁(写锁)模式
这些所有的信息加上线程本身被封装成一个内部类对象 Node
每个Node都有指向前驱节点和后继节点的指针,每个节点将有关线程的某些控制信息保存在其节点的前身中。
1.2 AQS内部类详解
1.2.1 Node
- 用作等待队列
static final class Node {
static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式
static final Node EXCLUSIVE = null; // 当前锁状态是 排它锁(写锁)模式
static final int CANCELLED = 1; // 表示线程被取消(执行过interrupt()方法或者 timeout )
static final int SIGNAL = -1; // 等待队列中 此线程后边的节点需要被 notify()、或者 LockSupport.unpark()【公平锁?】
static final int CONDITION = -2; // 此线程在一个 condition 等待队列中
static final int PROPAGATE = -3; // 暂时不知道是啥用(即使是头结点是共享锁模式好像也到排它锁的时候停止了)
/* 存储一个上边的某一个值,表示当前节点的状态 */
volatile int waitStatus;
// 当前节点的前一个节点,用来监视前一个节点的状态(如果前一个节点被取消,自己连接到前一个节点的前一个节点)
volatile Node prev;
// 当前节点的后一个节点,当这个节点当做头结点释放锁的时候,通知下一个节点
volatile Node next;
// 节点中保存的 线程
volatile Thread thread;
// 指向一个 condition等待队列
Node nextWaiter;
}
1.2.2 ConditionObject
public class ConditionObject implements Condition, java.io.Serializable {
// condition等待队列的第一个节点
private transient Node firstWaiter;
// condition等待队列的最后一个节点
private transient Node lastWaiter;
}
1.3 非公平模式图解
1.4 公平模式图解
1.5 线程被打断
1.6 进入Condition
等待队列
1.6.1
1.6.2
1.6.3 终于用上了nextWaiter
指针
1.7 AQS类变量详解
// 表示锁当前的状态,初始为0, ReentrantLock.lock()之后数字加1,表示已经上锁
private volatile int state;
// 等待队列的头结点(head节点初始化的时候,waitStatus会被赋值为0)
private transient volatile Node head;
// 等待队列的尾巴节点
private transient volatile Node tail;
// 通过 VarHander 直接获取上边三个变量的地址,直接使用变量地址来进行CAS操作
private static final VarHandle STATE;
private static final VarHandle HEAD;
private static final VarHandle TAIL;
static {
try {
/* handle 绑定到一个了个类的属性上,通过位于类的类型,变量的名字 和 变量本身的类型 */
MethodHandles.Lookup l = MethodHandles.lookup();
STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);
TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
1.8 插入到AQS队列
插入到AQS队列中只需要对当前的tail节点执行一次CAS操作
prev
主要用于处理线程取消(线程超时或者被打断)的情况。如果取消某个节点,则其后继节点(通常)会重新链接到未取消的前任节点。
加入队列就是原子操作更新tail
节点的next
对象
2 ReentrantLock
与AQS
2.1 lock()
获得锁
CAS更改state
的值,如果当前为0,线程1
给他加到了1,表示线程1
抢到了这把锁,其余线程进入等待队列,CAS更新tail
节点的next
指针
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取state的值
int c = getState();
if (c == 0) {
// 如果为0,CAS替换为1
if (compareAndSetState(0, acquires)) {
// 替换成功设置自己为独占线程
setExclusiveOwnerThread(current);
return true;
}
}
// 没有CAS成功则表示已有线程获得锁,
// 然后看看线程是不是自己(可重入)
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 把 state 的值 ++
setState(nextc);
return true;
}
return false;
}
// 如果获取锁失败(没抢到 + 自己也不是独占线程)
// 就要把自己加到等待队列中,等待队列的实现使用一个AQS的内部类实现,Node,
// 把锁模式设置为独占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
// CAS死循环往队尾加
Node oldTail = tail;
if (oldTail != null) {
// 用VarHandle给普通变量做CAS操作,把新申请的节点的pre设置为尾巴节点
node.setPrevRelaxed(oldTail);
// 再用 CAS 给尾巴的 next 设置 新node
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
// 当前节点时第一个进队等待的节点,初始化等待队列
initializeSyncQueue();
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
// 死循环,查看自己的前一个节点是否变成了头结点
final Node p = node.predecessor();
// 如果不是头结点直接短路,
// 是头结点就开始尝试获得锁
if (p == head && tryAcquire(arg)) {
// 获取锁成功,就把自己设置为头结点
setHead(node);
// 上一个已经执行完成,上一个出队的时候把他的next置位空
p.next = null; // help GC
return interrupted;
}
// 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?
// 第一次调用此方法之后前驱节点的值肯定被替换为了 -1 , 第二次调用的时候以true返回,进入if
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt(); // 调用lockSupport.park()阻塞线程
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
// 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// 前一个节点的状态是 -1 // 等待队列中 此线程后边的节点需要被 notify()
if (ws == Node.SIGNAL)
return true;
// 前驱节点已经被取消(打断或者超时)
if (ws > 0) {
do {
// 我就得找到没有被取消的前一个,看看他的状态
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 并把找到的没取消的next指针指向自己
pred.next = node;
} else {
// 前一个线程的状态肯定是 0 或者 PROPAGATE = -3;, 用CAS替换为 -1
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
可重入
可重入实现为给state
属性值往上加1
公平性
只有state
属性再次为0的时候表示线程1
彻底释放了锁资源,就可以通过上述过程来选择
-
非公平:新来的线程还在自旋状态的时候可以争抢锁资源,队列中的节点只激活头结点的下一个
-
公平:新来的线程也不可以抢锁
2.2 trylock()
尝试获取锁,获取不到锁或者过了超时时间就把自己设置为取消状态,并且更新前后节点的指向关系
curNode.next.pre = curNode.pre
curNode.pre.next = curNode.next
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
// 设置线程超时时间
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
try {
for (;;) {
// 这里跟普通获取锁的逻辑一样
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return true;
}
// 查看自己还能活多久
nanosTimeout = deadline - System.nanoTime();
// 已经超时,取消获取锁
if (nanosTimeout <= 0L) {
// 已经超时,取消获取锁,把自己取消,更改自己在等待队列中的前后关系
cancelAcquire(node);
return false;
}
if (shouldParkAfterFailedAcquire(p, node) &&
// 还有没有必要把线程阻塞?有可能阻塞过程执行的时间都要比他过期时间短,那我阻塞他还有什么用呢?
// 不如留着他自己过期的了
nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
2.3 lockInterruptibly()
节点中线程可以被打断,打断之后的线程也设置为取消状态,并且更新节点的指向关系
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 如果在其他线程中调用了该线程的 interrupt 方法,parkAndCheckInterrupt 就会返回true
//从而跳入 if 抛出异常
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
// 如果在其他线程中调用了该线程的 interrupt 方法,就会返回true
return Thread.interrupted();
}
2.4 condition.await()
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 新建一个 waitStatus = -2 (CONDITION)的节点
Node node = addConditionWaiter();
// 释放锁,并把现在已经加到队列中的线程激活
int savedState = fullyRelease(node);
int interruptMode = 0;
// node 是 CONDITION状态 进入while, 阻塞线程
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// acquireQueued 把自己阻塞
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
// lastWaiter 上一次被加到condition等待队列的节点
// t 为 空,则当前condition中没有节点等待,或者上一个等待节点的 waitStatus 已经被改变 (被取消)
if (t != null && t.waitStatus != Node.CONDITION) {
// 删除上一个节点,并继续把t设置为还在condition等待队列中的节点
unlinkCancelledWaiters();
t = lastWaiter;
}
// 新建
Node node = new Node(Node.CONDITION);
// 等待队列为空的话,自己就是第一个
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node; // 不为空的话就把自己连接在上一个后边
lastWaiter = node; // 自己是刚刚加进来最后等待的节点
return node;
}
// 调用await() 的时候释放锁,激活正在等待对列的线程
final int fullyRelease(Node node) {
try {
int savedState = getState();
// 调用await() 的时候释放锁,激活正在等待对列的线程
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}
// 调用await() 的时候释放锁,激活正在等待对列的线程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 调用await() 的时候释放锁,激活正在等待对列的线程
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
// ... 过滤代码(有可能头上的节点已经被取消,从队列头开始找到第一个没有取消的节点)
// unpark一个线程,这个线程不是某个等待队列中找的,而是依照在创建线程节点时的先后顺序
// 挑一个可以激活的进行激活操作,让后者获得此时的锁,
// 如果后者也会因为当前的系统状态阻塞,则继续park自己向后激活(使用的next指针向后找)
// condition 中使用的指针是 nextWaiter
if (s != null)
LockSupport.unpark(s.thread);
}
2.4 condition.signal()
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;
// 叫醒等待队列中的第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
// 叫醒第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
2.4 condition.signalAll()
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
// 第一个等待节点不为空,叫醒所有线程
// 循环叫醒所有线程,调用 LockSupport.unpark
if (first != null)
doSignalAll(first);
}
private void doSignalAll(Node first) {
// 因为此时要把所有线程叫醒了,所以把这两个变量置位空
lastWaiter = firstWaiter = null;
do {
// 循环叫醒该等待队列中的所有线程,调用 LockSupport.unpark
// 加到condition中调用的方法中 给 nextWaiter 赋值就是加到condition中的线程
// 虽然他们都有next指针,但是next指针是说他们创建节点时的顺序
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread); // 给线程unpark
return true;
}
3. ReentrantReadWriteLock
与AQS
3.1 特性
-
AQS 的状态
state
是32位,读锁用高16位,表示持有读锁的线程数(sharedCount),写锁低16位,表示写锁的重入次数 (exclusiveCount)。 -
状态值为 0 表示锁空闲。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZNe27oy-1613741042381)(md_imgs/ReentrantReadWriteLock & AQS.png)]
3.2 readLock.lock()
看完源码好像只有叫醒了队列中的后继节点,readLock可以直接(接下
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
接上)因为有读线程持有锁而直接跳过判断,返回业务代码直接执行。
-
此场景只适用于已经有读线程持有锁,后续读线程来到的时候可以直接跳过判断。
-
但对于读线程已经在等待队列中,就通过共享锁的设置叫醒后继节点,但是前一个节点还没执行结束,就已经把它挤出了队列,只要反正只要已经被挤出的线程能调用unlock方法就行。
-
下一个节点也是如此直接把自己设置为头结点,挤出上一个头结点,然后继续往下唤醒。一直唤醒到排它锁节点,就得让刚刚挤出的所有线程执行完毕才可以继续执行排他线程代码。
3.3 小实验
/*
* @Author 郭学胤
* @University 深圳大学
* @Description
* @Date 2021/2/8 19:28
*/
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class L34_ReadWriteLock {
static ReentrantLock reentrantLock = new ReentrantLock();
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static int num;
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void main(String[] args) throws InterruptedException {
L34_ReadWriteLock test = new L34_ReadWriteLock();
/* 使用读写锁,读线程可以同时进行 */
Runnable task1 = () -> test.read(readLock);
Runnable task2 = () -> test.write(writeLock, new Random().nextInt(50));
/*
* 首先启动4个读线程,每个读线程都是 exclusive
* 每个读线程睡1s钟,三个执行完需要 4 秒钟
*
* E -> E -> E -> E
* 队列状态如上所示
* */
createWriteThread(task2, 4, "write");
/*
* 主线程睡1s,保证现在是读线程在占着锁
*
* */
Thread.sleep(1000);
/*
* 启动 10 个读线程,因为此时是 写线程在占用锁,
* 所以读线程已经加到了队列中
* 此时前边三个写线程全部执行完至少还剩 3s
*
* E -> E -> E -> R -> ... -> R
* */
createReadThread(task1, 10, "read");
Thread.sleep(1000);
/*
* 再启动 3 个 写线程,前三个中的某个写线程还没有释放锁
* 所以这三个线程也会加入队列
* 此时前边三个写线程全部执行完至少还剩 2s
*
* E -> E -> E -> R -> ... -> R -> E -> E -> E
* */
createWriteThread(task2, 3, "write");
/*
* 主线程睡1s,保证现在是读线程在占着锁
* 此时前边三个写线程全部执行完至少还剩 1s
* */
Thread.sleep(1000);
/*
* 启动 10 个读线程,因为此时是 写线程在占用锁,
* 所以读线程已经加到了队列中
*
* E -> E -> E -> R -> ... -> R -> E -> E -> E -> R -> R ....
* */
createReadThread(task1, 10, "read");
/*
* 最后的执行顺序,请大家自己打印看
* 前四个 写锁
* 十个 读锁
* 三个 写锁
* 十个 读锁
* */
}
public void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over");
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void write(Lock lock, int val) {
try {
lock.lock();
Thread.sleep(1000);
num = val;
System.out.println("write over");
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public static void createWriteThread(Runnable task2, int i2, String write) {
for (int i = 0; i < i2; i++) {
new Thread(task2, write + i).start();
}
}
public static void createReadThread(Runnable task1, int i2, String read) {
for (int i = 0; i < i2; i++) {
new Thread(task1, read + i).start();
}
}
}
实验结论分析
结论一
- 每次读锁获得锁之后并不是把队列中所有的读锁unpark
- 读锁变成头结点之后获取到锁之后,unpark下一个读锁的节点
- 下一个读锁的节点获取锁之后,把之前头结点挤出队列,把自己设置为头节点,然后继续往下unpark下一个读锁节点
- 当下一个是写锁节点的时候,仍然unpark下一个节点,但是写锁节点暂时获取不到锁资源,需要等待所有的读锁释放资源
结论二
- 非公平锁只是说新来的线程,仍在自旋中的线程可以尝试获取锁
- 一旦加到了队列中,就算是非公平锁也仍然需要前驱节点unpark
3.4 源码阅读
3.4.1 readLock.lock()
// 读锁获取,共享锁
public void lock() {
sync.acquireShared(1);
}
private void doAcquireShared(int arg) {
// 先给自己注册共享模式的节点
final Node node = addWaiter(Node.SHARED);
boolean interrupted = false;
try {
for (;;) {
// 获取前一个节点是否是头结点,如果前一个节点时头节点
final Node p = node.predecessor();
if (p == head) {
// 开始获取共享锁
// 如果获取共享锁成功就开始
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
return;
}
}
// unpark之后,继续for
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
} finally {
if (interrupted)
selfInterrupt();
}
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 先查看当前有无排它锁,有的话直接返回 -1,说明获取共享锁失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 没有排它锁,获取当前共享锁数量,并往共享锁的数量上 + 1
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
// 如果没有读锁,自己就是第一个读锁
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 读锁重入
firstReaderHoldCount++;
} else {
// 如果第一个读线程不是自己,现在就要给总读线程数上+1了
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
// 此时就已经获取到了共享读锁
return 1;
}
return fullTryAcquireShared(current);
}
private void setHeadAndPropagate(Node node, int propagate) {
// 把自己设置为头结点,然后查看自己后边的节点是否是共享模式
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
// 如果是共享模式,unpark后边的线程
doReleaseShared();
}
}
private void doReleaseShared() {
for (;;) {
//唤醒操作由头结点开始,注意这里的头节点已经是上面新设置的头结点了
//其实就是唤醒上面新获取到共享锁的节点的后继节点
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//表示后继节点需要被唤醒
if (ws == Node.SIGNAL) {
//这里需要控制并发,因为入口有setHeadAndPropagate跟releaseShared两个,避免两次unpark
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
//执行唤醒操作
unparkSuccessor(h);
}
//如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
//如果头结点没有发生变化,表示设置完成,退出循环
//如果头结点发生变化,比如说其他线程获取到了锁,为了使自己的唤醒动作可以传递,必须进行重试
if (h == head)
break;
}
获取state的值,查看有无排它锁
有排它锁
- 自旋等待
- 创建一个共享模式节点用CAS操作添加到等待队列末尾
- 调用
LockSupport.park()
阻塞线程
无排它锁
- 共享锁数量+1,继续执行代码(或者直接跳出刚刚那个判断语句,直接开始执行代码)
4.2 readLock.unlock()
-
普通读线程执行的之后,把state数量减-1
-
最后一个读线程退出的时候,激活等待队列中的线程(此时队列中第一个肯定是写线程)
4.3 writeLock.lock()
没有读线程 + 无读线程
setExclusiveOwnerThread
为自己
有其他读线程
把自己加入到等待队列中,并且锁模式设置为排它锁
4.4 writeLock.unlock()
setExclusiveOwnerThread
为null
,更新state
数值
3. CountDownLatch
与AQS
state为门栓,当门栓为0时,表示线程不用被阻塞可以继续运行。
3.1 await()
-
获取state是否为0,为0则可以直接继续执行,不用阻塞当前线程
-
如果获取到当前门栓值大于0,阻塞当前线程
-
创建一个共享锁模式的Node,用CAS操作添加到等待队列队尾
-
调用
LockSupport.park()
阻塞线程 -
更新等待队列的头结点为刚刚创建的节点,并把头结点的
waitStatus
设置为propgate
-
3.2 countDown()
-
CAS操作更新
state
的值,因为值大于0导致其他线程阻塞,所以在每次调用state--
-
当最后把
state
更新为0的时候,unparkSuccessor
,解除头结点封印,又因为各个线程都是共享锁,所以一旦头结点执行,所有的等待队列中的线程均可以激活,周而复始的把自己设置为头结点,然后挤出原有的头结点
5. Varhandle
- 普通属性进行原子操作
- 比反射快,直接操纵二进制码