AQS及其实现类源码阅读
AQS
参考https://mp.weixin.qq.com/s/trsjgUFRrz40Simq2VKxTA
AQS是定义了一个抽象的队列同步器,java并发包中几乎所有的工具都依赖于AQS实现,它由一个整形状态state和一个
先入先入的队列组成,线程进来时会判断state状态,并尝试用cas来修改state来决定是否能获取锁,还是加入队列.
AQS实现了顶层的框架,框架中定义了CAS,CAS失败如何加入队列,线程执行完如何唤醒队列中的线程等等操作.
而具体的工具类则只需要实现getState(),tryAcquire(), tryAcquireShare(),tryRelease(),tryAcquireShared()就可以自定义一个并发工具类.而且因为这些方法并不是抽象的,所以子类只用选择这些方法中的一部分重写就可以.
setState(),getState(): 设置/获取当前同步器状态
tryAcquire(),tryRelease(): 排他方式获取/释放执行权限
tryAcquireShare(),tryAcquireShared(): 共享方式获取/释放执行权限
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
- state来标明当前同步器的状态,具体state等于多少获取锁都由子类实现
/**
* The synchronization state.
*/
private volatile int state;
- 队列节点Node,表示每个执行的线程
static final class Node {
////***** 独占/共享模式 *****////
//Node nextWaiter;
//标志该Node是共享模式
static final Node SHARED = new Node();
//标志该Node是独占模式
static final Node EXCLUSIVE = null;
////***** Node节点的waitStatus状态枚举 *****////
//当前节点状态 非负节点表示不需要被唤醒,所有的修改都使用CAS
volatile int waitStatus;
//取消状态,表示该节点获取锁超时,锁着线程被中断
static final int CANCELLED = 1;
//表示存在后继结点在等待当前结点唤醒。后继结点入队时,会将前继结点的状态更新为SIGNAL。
static final int SIGNAL = -1;
//表示该节点在等待CONDITION,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。
static final int CONDITION = -2;
//共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。
static final int PROPAGATE = -3;
//指向前继节点
volatile Node prev;
//指向后继节点
volatile Node next;
//指向当前线程
volatile Thread thread;
//返回当前节点是独占模式,还是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
//返回当前节点的前继节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
////**** 构造函数 ****////
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
-
tryAcquireNanos 尝试获取执行权限(非共享)
- 先调用子类实现的tryAcquire(),判断是否能获取执行权限,可以则直接返回ture
- 否则调用doAcquireNanos()方法
- doAcquireNanos(),首先构建一个Node节点,并将该节点插入到队列的队尾
- 之后死循环判断当前节点是否能够执行
- 如果当前节点的前置节点为头节点,并且该节点获取执行权限成功,则将该节点置为头结点
- 如果获取执行权限失败,则将该节点加入队列中(CAS的方式),使用park()方法将该线程置为等待状态,并将该节点的前置节点的状态置为SIGNAL,意味着如果前置节点执行完成release时会唤醒后置节点
源码如下:
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
//首先判断线程是否中断,已中断则抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquire,由子类实现,如果子类返回>0 则直接获取执行权限,否则执行doAcquireNanos
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
//判断阻塞时间
final long deadline = System.nanoTime() + nanosTimeout;
//构建一个排他节点node,并将该节点插入到队列队尾
final Node node = addWaiter(Node.EXCLUSIVE);
//标记最终获取执行是否失败
boolean failed = true;
try {
//死循环尝试获取锁,获取失败加入队列
for (;;) {
//获取p的前置节点
final Node p = node.predecessor();
//node的前一个节点p是否是头结点,如果是头结点,则尝试获调用子类tryAcquire()的方法取执行权限
if (p == head && tryAcquire(arg)) {
//如果获取执行权限成功,说明前一个节点p已经执行完成,node获得执行权限
//这时吧node设置为头结点
setHead(node);
//释放掉之前的头结点
p.next = null; // help GC
//将failed标记为false,表示最终获取执行权限成功
failed = false;
//返回获取执行权限成功
return true;
}
//每次循环都会计算是否超过最大等待时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
//超过则标明最终获取执行权限失败,直接返回false
return false;
//判断node的前置节点状态,如果前置节点状态等于SIGNAL,返回true
//如果前继节点状态为CANCEL,则将该节点挪到非CANCEL状态节点后,并返回fasle(没有判断和修改前继节点状态,该节点还不能进入等待状态)
//否则将前置节点状态置为SIGNAL,表示新插入的node在等待获取执行权限,并返回ture
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
//将前置节点已置为SIGNAL,则将该当前线程置为等待,等待前置线程的唤醒
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
//如果最终获取执行权限失败,则将该线程状态置为CANCEL
//并用CAS的方式将node从链表中删除
//如果node是head的后继节点,则直接唤醒node的后继节点,让其尝试获取锁
if (failed)
cancelAcquire(node);
}
}
- acquireShared 共享的方式获取执行权限,即允许多个线程同时获取执行权限
- 调用子类实现的tryAcquireShared()获取剩余资源数量,剩余资源数量>0 返回true
- 剩余资源不足(超时或者被中断)则调用doAcquireShared()
- doAcquireShared()中死循环尝试获取执行权限
- 先创建节点,并将节点加入到队尾,与上面独占方式不同的是该节点模式设置为共享模式
- 如果前继节点为头节点,则再次调用子类实现的tryAcquireShared()来获取剩余资源,tryAcquireShared()>0表示还有剩余允许其他线程执行
- 获取执行权限成功,后继节点是否还可以继续执行(判断节点状态和剩余资源数量),可以的话调用doReleaseShared()唤醒后继节点
- 获取执行权限失败,和独占模式一样,判断是否需要将当前线程置为等待,需要的话则将当前线程置为等待,并修改前置线程waitStatus为SIGNAL,如果前置节点执行时会判断是否可以唤醒后置节点(步骤6)
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//调用子类实现的tryAcquireShared()方法判断是否可以获取执行权限(返回值大于0),获取失败则调用doAcquireShared()
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
//同样将创建节点,并将节点加入到队尾,不同的是该节点模式设置为共享模式
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//获取前继节点
final Node p = node.predecessor();
if (p == head) {
//如果前继节点为头节点,则再次调用子类实现的tryAcquireShared()来获取执行权限
//返回值大于0表示出当前节点外,还允许其他节点执行
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取执行权限成功
//1.记录当前head节点h
//2.将该节点置为head节点
//3.调用tryAcquireShared(arg)的获取剩余资源r,和头节点的waitStatus
//4.如果r>0 && waitStatus<0&&并且后继节点为共享模式节点,表示后继节点可以被唤醒执行,则调用doReleaseShared(),唤醒后置节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
//获取执行权限失败,和独占模式一样,判断是否需要将当前线程置为等待
////需要的话则将当前线程置为等待,并修改前置线程waitStatus为SIGNAL
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
//和独占式一样,如果失败则将该节点状态置为CANCEL,并从队列中将该节点删除
cancelAcquire(node);
}
}
-
acquireShared() 不带超时时间的共享的方式获取执行权限
和tryAcquireSharedNanos的区别只是没有返回值,如果没有获取到执行权限,则当前线程一直等待
//没有返回值,线程如果没有获取到执行权限则一直等待,获取到则线程可以继续往下执行 public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { //判断线程是否在等待过程中被中断 boolean interrupted = false; for (;;) { //获取该节点的前继节点 final Node p = node.predecessor(); if (p == head) { //去过前继节点为头节点,则再次调用子类实现的tryAcquireShared()来获取执行权限 //返回值大于0表示出当前节点外,还允许其他节点执行 int r = tryAcquireShared(arg); if (r >= 0) { //获取执行权限成功 //1.记录当前head节点h //2.将该节点置为head节点 //3.判断ryAcquireShared(arg)的返回值r,和h的waitStatus //4.如果r>0 && waitStatus<0&&并且后继节点为共享模式节点,表示后继节点可以被唤醒执行,则调用doReleaseShared(),唤醒后置节点 setHeadAndPropagate(node, r); p.next = null; // help GC //如果线程在等待时已经被中断,则中断该线程 if (interrupted) selfInterrupt(); failed = false; return; } } //获取执行权限失败,和独占模式一样,判断是否需要将当前线程置为等待 ////需要的话则将当前线程置为等待,并修改前置线程waitStatus为SIGNAL //唤醒时会判断线程是否处于中断状态,如果被中断则将interrupted = true if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) //和独占式一样,如果失败则将该节点状态置为CANCEL,并从队列中将该节点删除 cancelAcquire(node); } }
-
release方法 释放执行权限
基本逻辑就是调用子类重写的tryRelease方法释放执行权限
释放成功则判断头结点和后继节点状态决定是否和唤醒哪个后继节点
public final boolean release(int arg) {
//调用子类重写的的tryRelease方法,如果释放失败则返回false
if (tryRelease(arg)) {
Node h = head;
//判断头节点状态,如果头节点状态不为0则尝试唤醒后继节点
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//唤醒后继节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
//头结点状态小于0时,CAS将头结点状态替换为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
//找到后继节点中第一个waitStatus<0的后继节点
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);
}
- releaseShare 释放共享锁,
public final boolean releaseShared(int arg) {
//调用tryReleaseShared(arg)释放资源成功
if (tryReleaseShared(arg)) {
//如果释放资源成功,doReleaseShared()主要用于唤醒后继节点
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
//如果头节点状态为SIGNAL,表示有后继节点在等待唤醒,则CAS尝试头结点的状态替换0
//CAS失败重新循环
//CAS成功唤醒后继节点
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//CAS成功则唤醒头结点的后继节点
unparkSuccessor(h);
}
//否则如果头结点状态为0,为0说明后继节点已经被唤醒,则尝试将头结点状态替换为PROPAGATE(传播)
//CAS失败continue
//替换为PROPAGATE表示该节点唤醒操作可以传播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
//如果头结点已经被替换,则重新循环
if (h == head) // loop if head changed
break;
}
}
总结:
AQS中主要的两个属性一个是标志位state, 一个是等待队列
state标志位,是用来判断是否有剩余资源可以获取的,具体state的修改规则由子类重写tryAcquire()和tryRealease()等方法来定义的.对state的修改都使用CAS的方式修改.
对于共享模式,tryAcquireShare()>0 表示还有剩余资源,对于独占模式tryAcquire()则返回本次是否获取到了执行权限.
等待队列是一个双向链表实现的先入先出队列,其节点Node中有个一个重要的属性waitStatus, waitStatus>0 表示该线程节点不需要被唤醒,waitStatus=<0 表示该节点线程需要被唤醒,初始状态是0.比较重要的一个状态是 static final int SIGNAL = -1 , 表示该节点的后继节点需要被唤醒,每次从队尾插入新节点时就把把新节点的前继节点的waitStatus设为SIGNAL,表示有后继节点需要被唤醒.Node还区分共享模式和非共享模式.
在等待队列中头结点表示获取到执行权限正在执行的线程节点,其他线程都在等待队列中挂起,对于独占模式,当头结点执行完成执行release()后,会唤醒队列中第一个waitStatus<=0的元素,让其再次执行中的tryAcquire()死循环然后尝试获取执行权限.获取到之后会把当前节点替换成头节点,然后把头结点释放掉.
对于共享模式与独占模式不同的是,只要还有tryAcquireShare()>0就可以一个个的唤醒队列中的waitStatus<0.并且是共享模式的节点.
此外AQS中还有一个字段exclusiveOwnerThread,来表示当前持有执行权限的独占线程,这个可以用于锁的可重入
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
还有可以从上面的代码看出,AQS是支持中断的,线程中断后,下次被唤醒时会判断是否被中断,被中断的话则把这个线程节点状态置为CANCEL =1
Unsafe.park()/Unsafe.unPark()
上面介绍AQS时,线程在加入到等待队列时都会调用Unsafe.park()来把线程挂起,而唤醒时则使用Unsafe.unPark()唤醒线程.这两个方法和wait()/notify()的区别是,这两个用法的方法不用获取锁.
CountDownLatch
- CountDownLatch中实现了一个内部类Sync继承至AQS ,Sync中的构造函数要传入一个整数来赋值给Aqs中的state,state>0表示可以共享锁的线程数量
- 内部类Sycn重写了AQS中的tryAcquireShared,tryReleaseShared方法
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造函数传入一个整数来赋值给AQS中的state,state>0表示可以共享锁的线程数量
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//获取共享锁
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//释放锁,cas尝试将state-1
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
//注意这里state等于0时就不会再减了,所以state不会减为0
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
- await()方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
其实比较简单,就是尝试获取共享锁,没什么好说的
- countDown()方法,其实就是释放锁
public void countDown() {
sync.releaseShared(1);
}
- 总结
原理比较简单,初始化时将aqs的state设置为CountDown初始化时传入的需要等待线程执行的数据. state>0 表示可以共享锁的线程数量.
主线程调用的await()方法,是调用父类的tryAcquireSharedNanos()方法尝试获取执行权限,而是否能获取执行权限的方法tryAcquireShared被Sync重写,只有当state==0时,主线程才能获取执行权限.否则说明主线程还不能执行.否则,主线程会被加到Aqs中的等待队列中等待.
子线程调用的countDown()方法调用releaseShared(1)来释放执行资源,将state-1.当state减到0时,会唤醒在等待队列中的主线程,唤醒成功之后主线程就可以继续执行了.
- state会不会减为负数,而导致主线程一直阻塞?
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 10; i++) {
int index = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(index);
countDownLatch.countDown();
}
}).start();
}
Thread.sleep(1000);
countDownLatch.await();
System.out.println("执行完成");
}
}
输出:
2
0
3
5
1
4
6
8
9
7
执行完成
从结果看 线程数量大于CountDown,最终主线程也会被执行.
并且仔细看tryReleaseShared的代码,如果state===0时,再次执行countDown方法,就会返回false,而不会将state减为负数.
并且只要有5个线程countDown之后,主线程就可以继续执行了.
Semaphore信号量
信号量用于控制可以允许执行的线程的数量
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++) {
int index = i;
//同时只能有5个线程执行
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(index + "线程执行了");
Thread.sleep(1000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
执行结果:
1线程执行了
2线程执行了
0线程执行了
4线程执行了
9线程执行了
//停顿
5线程执行了
8线程执行了
6线程执行了
7线程执行了
3线程执行了
- Sync
内部实现了一个抽象的Sync类继承AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
//premites 表示允许执行的线程数量
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
//非公平方式获取执行权限,即抢占式获取执行权限
//每个线程在加入到队列前都会先尝试获取一次执行权限,获取失败采取加入到AQS中的队列中
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
//剩余资源 = 当前可用资源 - 本次请求资源
int remaining = available - acquires;
//这里有三种情况
//1. remaining<0 说明当前剩余资源不足,则返回remaining
//2, remaining >=0 说明剩余资源充足,则去CAS将remaining替换state,替换成功返回remaining
//3. remaining >=0 并且CAS修改state失败,上层AQS的doAcquireShared()会把这个线程加入到等待欲裂中,等待其他线程释放资源后唤醒后,再次尝试获取执行权限
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//重写AQS的释放共享锁的方法,基本就是将state 修改为 state + 释放的资源数量
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//获取当前state
int current = getState();
//next = 当前state + 本次释放的资源数量
int next = current + releases;
//如果next < state 抛出ERROR
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//CAS修改state = next
if (compareAndSetState(current, next))
return true;
}
}
//减少允许并行执行的资源数量,也是cas 修改state
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
//将允许并行执行的资源置为0,情况资源池
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
- 根据线程是否公平抢占资源 Semaphore内部实现了一个公平的FairSync和一个非公平的NonfairSync,来实现Sync.这两个类的区别只是重写了tryAcquireShared()
- 对于NonfairSync,其重写tryAcquireShared()内部调用nonfairTryAcquireShared(),每次有新的线程想获取执行权限时,都会先尝试获取一次资源,成功直接获取执行权限成功,失败了才会加入队列
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
- 对于FairSync,重写tryAcquireShared(), 只要当前队列还有元素,并且头元素持有的不是当前线程,就会直接把元素加入队列
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
//1.如果等待队列头结点等于尾节点,返回false,表明当前线程可以循环尝试获取资源,不用加入队列
//2.如果队列中存在除头节点之外的其他元素,并且头结点的后继元素持有的不是当前线程,则返回-1,让该线程加入到等待队列中
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
公平和非公平的区别只是在于,新线程进来时会不会先获取资源一次,非公平模式无论队列是有有其他线程等待,都会先获取一次执行权限,失败了才会加入队列. 公平模式则会判断队列是否有线程等待,如果有线程等待,则会吧线程加入到队列中等待.
默认是非公平模式
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
-
总结
Semaphores主要是重写了AQS的tryReleaseShared(),线程以共享模式获取执行权限,并实现了公平和非公平两种抢占模式.
初始将state设置为同时允许执行线程的数量.当state>0 线程可以获取到执行权限,并使用CAS的方式修改state = state -1,
当state<=0 说明资源不足,则将线程加入到等待队列中.
释放资源时则CAS 修改state=state+1
公平和非公平的区别在于新线程进来是否会先抢占一次锁.默认非公平模式
ReentrantLock
ReentrantLock同样基于AQS实现,同样也有公平和非公平的模式
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
//非公平的获取锁
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取state
int c = getState();
//如果state == 0,表示当前锁还没有被持有,当前线程可以尝试获取锁
if (c == 0) {
//尝试CAS将state替换成1,state>0表示当前有线程正在持有锁,以及获取了多少次锁
if (compareAndSetState(0, acquires)) {
//替换成功则表示获取到独占资源,然后把AQS中记录的独占线程替换成当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//如果当前线程等于AQS中记录的当前持有锁的线程,基于可重入,则直接获取执行权限,并给state+1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//获取锁失败,返回false,加入等待队列
return false;
}
//释放锁
protected final boolean tryRelease(int releases) {
//线程每次释放则将state-1
int c = getState() - releases;
//如果当前线线程不等于AQS中记录的当前持有锁的线程,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//将exclusiveOwnerThread置为null
free = true;
setExclusiveOwnerThread(null);
}
//修改state = state-1
setState(c);
//返回是否释放成功
return free;
}
//判断当前线程是否持有锁
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();
}
//创建一个新的Condition
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
//获取当前持有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//返回当前线程冲入了多少次,当前线程未持有锁返回0
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//判断是否有线程已持有了锁
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
//从流中重新构建该实例,将锁重置为未加锁状态
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
- ReentrantLock也有公平和非公平模式,所以实现了NonfairSync和FailSync继承自Sync, 默认是非公平的
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
NoFairSync 抢占式获取锁,加入队列前会先尝试获取一次锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//调用Sync中的nonfairTryAcquire,
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire,(acquires);
}
}
FairSync 非抢占式
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
//大概意思就是先判断当前锁是否被占用,没有的话尝试获取锁.如果被占用判断是否是当前线程占用
//current == getExclusiveOwnerThread()的话表示可重入, state
//都失败的话加入队列等待
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;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
- lock 和 tryLock
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
- Condition
ReentrantLock主要是用来解Object.wait/notify/notifyAll难以使用的问题.并且同一个锁支持多个Condition,并且这些Condition都绑定在同一个锁上
public interface Condition {
//下面方法都是让线程等待,对应Object.awit()
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
//这俩方法用于唤醒线程,对应Object.notify() 和Object.notifyAll()
void signal();
void signalAll();
}
- ConditionObject 类 是Condition在AQS中的一个实现类
ConditionObject 内部定义了两个字段firstWaiter,lastWaiter,分别表示指向条件队列的对头元素和队尾元素,Node是AQS中定义的Node,和等待队列中的Node是同一个类
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
.....
}
- condtion.await()
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//新建并添加节点到条件队列,如果队尾节点已经是取消状态(等待超时/被中断),从头结点开始清除所有不符合状态(已取消)的节点
Node node = addConditionWaiter();
//线程能执行到这,说明该线程已经获得AQS的执行权限(锁),然后调用了await()方法
//并且上面的addConditionWaiter方法已经把该线程加入到条件队列中等待.
//所以需要释放该线程已获取的AQS的执行权限(锁),调用release()方法,释放失败则抛出异常
int savedState = fullyRelease(node);
//中断标志
int interruptMode = 0;
//循环判断当前节点是否是在条件队列中,在的话把该线程挂起,等待唤醒
//被唤醒后会把线程从条件队列中删除,并重新加入到AQS的等待队列中,循环终止
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//****线程被唤醒后的操作****//
//acquireQueued获取锁并返回线程是否中断
//如果线程被中断,并且中断的方式不是抛出异常,则设置中断后续的处理方式设置为REINTERRUPT
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//从头到尾遍历Condition队列,移除被cancel的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//如果线程已经被中断,根据上面获取的中断处理方式,处理中断
//THROW_IE 抛出异常
//REINTERRUPT 中断当前线程
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
//新建并添加节点到条件队列,如果队尾节点已经是取消状态(等待超时/被中断),从头结点开始清除所有不符合状态(已取消)的节点
private Node addConditionWaiter() {
//判断队尾节点状态,如果队尾节点已经是取消状态(等待超时/被中断),从头结点开始清除所有不符合状态的节点
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
//从头结点开始清除节点
//从头结点开始判断节点状态是否是Node.CONDITION,
//不是的话将该节点删除
unlinkCancelledWaiters();
t = lastWaiter;
}
//新建节点,waitStatus = Node.CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//插入到条件队列中
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}