1、AQS是什么
有协作功能的一些类,如ReentrantLock和Semaphore,CountDownLatch、ReentrantReadWriteLock,它们底层都实现了一个基类,这就是AQS
AQS可以理解为是一个工具类,帮助我们实现线程之间的协作
2、AQS演示
public class Semaphore implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
都是一个Sync内部类,去继承我们的AbstractQueuedSynchronizer(AQS)类,我们其他的类也是这个套路
3、AQS原理
AQS由三个核心部分组成:
• State
• 控制线程强锁和配合的FIFO队列
• 期望协作工具类去实现的获取/释放等重要的方法
3.1、State
state的具体含义,会根据具体实现类的不同而不同,比如在Semaphore中,他表示“剩余的许可证的数量”,而在CountDownLatch中,它表示“还剩余倒数的数”
state是volatile修饰的,会被并发地修改,所以素有修改state的方法都需要保证线程安全,比如getState,setState已经compareAndSetState操作来读取和更新这个状态,这些方法都是依赖于atomic包的支持
//通过unsafe顶层指令来保证原子性
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
在ReentrantLock中,state是用来表示锁的占有情况,包括可重入计数,当state的值为0的时候,标识该Lock不被任何线程锁占有
3.2、FIFO队列
这个队列是用来存放“等待的线程”,AQS就是“排队管理员”,当多个线程竞争锁的时候,只有一个线程可以获取,其他的线程串在一起进行等待。当锁释放时,锁管理器就会挑选一个合适的线程来占有刚刚释放的锁
ASQ会维护一个等待的线程队列,把线程都放到这个队列中,且这个队列是双向链表
3.3、获取/释放的方法
这个需要实现类自己实现,会依赖state的值,经常会阻塞(获取不到锁的时候)
3.4、AQS的实现类
1. 写一个类,想好协作的逻辑,实现获取/释放的方法
2. 内部写一个Sync类继承AbstractQueuedSynchronizer
3. 根据是否独占重写tryAcquirte/tryRelease或tryAcquireShared(int acquires)和tryReleaseShared(int releases)等方法,在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法
4、CountDownLatch源码分析
构造方法
//构造方法
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//调用sync的构造
Sync(int count) {
etState(count);
}
//设置state值
protected final void setState(int newState) {
state = newState;
}
getCount方法
//调用getCount方法
public long getCount() {
return sync.getCount();
}
int getCount() {
return getState();
}
//最终返回的是state 的值
protected final int getState() {
return state;
}
countDown方法
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
//唤醒队列中的所有线程
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
//死循环
for (;;) {
//获取到state的值
int c = getState();
if (c == 0)
return false;
//如果不为0,说明还可以减
int nextc = c-1;
//用原子操作保证线程安全
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
await方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//判断state是不是0
if (tryAcquireShared(arg) < 0)
//进入到阻塞队列中
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
• 调用CountDownLatch的await方法时,便会尝试获取“共享锁”,不过一开始是获取不到该锁的,于是线程被阻塞
• 而“共享锁”可获取到的条件,就是“锁计数器”的值为0
• 而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1
• count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待获取线程的共享锁的线程才能继续运行
5、Semaphore源码分析
获取许可证
public void acquire(int permits) throws InterruptedException {
//判断需要获取的许可证个数不能小于0,否则抛出异常
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
//小于0,就去等待
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
//返回值如何小于0,获取许可证失败
final int nonfairTryAcquireShared(int acquires) {
//死循环自旋
for (;;) {
//getState()获取许可证数量
int available = getState();
//判断当前许可证-获取许可证的值
int remaining = available - acquires;
if (remaining < 0 ||
//如果获取的值够,就会进行cas操作保证线程安全
compareAndSetState(available, remaining))
return remaining;
}
}
• 在Semaphore中,state表示许可证的剩余数量
• 看tryAcquire方法,判断nonfairTryAcquireShared大于等于0的话,代表获取成功
• 这里会先检查剩余的许可证数量够不够这次需要的,用减法来计算,如果不够,就返回负数,如果够了,就用cas操作自旋来改变state的状态,知道改变成功返回整数;期间如果被其他人修改了导致获取为负数,也会直接返回负数代表失败
释放许可证
public void release(int permits) {
//判断释放的许可证,小于0抛出异常
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
protected final boolean tryReleaseShared(int releases) {
//自旋
for (;;) {
int current = getState();
int next = current + releases;
//判断当前的state+需要释放的许可证不能大于最大允许数,大于就抛出异常
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//使用cas操作,对state进行设置
if (compareAndSetState(current, next))
return true;
}
}
6、ReentrantLock源码分析
解锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
//tryRelease()代表锁是否被释放了
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒后面的节点
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//getState()获取当前的次数,这个就是当前的次数减一
int c = getState() - releases;
//判断当前线程是不是持有锁的线程,不是就抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//当前次数减完一后,如果为0,就释放锁
if (c == 0) {
//free代表锁是不是自由的
free = true;
setExclusiveOwnerThread(null);
}
//不为0,则代表是重入过,只是state-1操作
setState(c);
return free;
}
• 由于可重入的,所以state代表重入的次数,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是抛出异常;如果是的,重入的次数减一,如果减到0,就说明完全释放了,于是free就是true,并且将state设置为0
加锁
final void lock() {
if (compareAndSetState(0, 1))
//将当前的线程设置为持有锁的线程
setExclusiveOwnerThread(Thread.currentThread());
else
//
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
//addWaiter()添加到队列中
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//等于0代表没有线程持有
if (c == 0) {
if (!hasQueuedPredecessors() &&
//通过cas操作来获取到这把锁
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//我这个线程正好是持有这把锁,是一个重入的操作
else if (current == getExclusiveOwnerThread()) {
//重入的次数+1
int nextc = c + acquires;
//判断重入后的值是不是溢出
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//将state值设置进去
setState(nextc);
return true;
}
//代表锁被其他线程持有,直接返回false
return false;
}
7、尝试利用AQS实现自己的Latch门闩
一次性门闩
public class OneShotLatch {
private final Sync sync = new Sync();
//获取
public void await(){
sync.acquireShared(0);
}
//释放
public void signal(){
sync.releaseShared(0);
}
//不需要强制重写,是因为不确定我们需要重写哪些方法
static class Sync extends AbstractQueuedSynchronizer {
@Override
protected int tryAcquireShared(int arg) {
return (getState() == 1 ) ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
setState(1);
return true;
}
}
public static void main(String[] args) {
final OneShotLatch oneShotLatch = new OneShotLatch();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "尝试获取门闩,获取失败那就等待");
oneShotLatch.await();
System.out.println( "开闸放行" +Thread.currentThread().getName() + "继续运行");
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
oneShotLatch.signal();
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");
oneShotLatch.await();
System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");
}
}).start();
}
}