1.AQS概述
AQS,AbstractQueuedSynchronizer,即队列同步器。它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),JUC并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。它是JUC并发包中的核心基础组件,相比synchronized,synchronized缺少了获取锁与释放锁的可操作性,可中断、超时获取锁。
2.Node-AQS内部类
在 AQS内部,其实是通过维护一个FIFO的双向队列来实现线程安全的,双向队列内部元素类型为Node;,如下图所示,为Node内部类相关的方法以及属性,下面对相关方法和属性做简要分析

1. SHARED,EXCLUSIVE
表示线程是因什么被挂起,从而放入aqs等待队列中去的,shared表示获取共享资源被放到aqs队列中,而exclusive是因为获取独占资源被放入等待队列中去。
2.waitStatus
表示线程等待状态,值的改变是通过cas算法修改的。
3.CANCELLED,SIGNAL,CONDITION ,PROPAGATE
表示线程状态取值,各个值的含义如下:
- CANCELLED:线程被取消,一般是因为超时或者被打断,此种类型的线程,是永远不会被 阻塞的。
- SIGNAL:线程需要被唤醒,一般是通过调用acquire让线程的标志为该状态,如果获取失败,则线程被阻塞。
- CONDITION:线程在同步队列中。
- PROPAGATE: 释放共享资源时需要通知其他节点
4.prev
记录当前节点的前驱节点。
5.next
记录当前节点的后继节点 。
3.AQS内部重要方法
在 AQS 中 维持了 一 个 单 一 的状态信息 state,可以通过 getState 、 setState 、compareAndS etState 函数修改其值了;锁的释放和获取其实都是围绕state值的变化。下面为aqs在获取独占资源的时候,获取锁的步骤模板。需要注意的是 , AQS 类并没有提供可用的TryAcquire 和TryRelease 方法,对于state表示的意思,也是由子类自己定义的; 如Reentran tLock 的 实 现来说, state 可以用 来表示当前线程锁的重入次数,对于 读写锁 ReentrantReadWriteLock 来说 ,state 的 高16位表示读状态,也就是获取该读锁的次数,低 16 位表示获取到写锁的线程的可重入次数
1.获取独占资源
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当一个线程调用acquire获取资源的时候,首先会调用tryAcquire方法尝试获取资源,具体的操作是设置state变量的值,成功则返回,失败则将该线程封装为Node.EXCLUSIVE的节点到aqs队列中去(入队列方法请继续看下面),并调用LockSupport.park(this) 方法挂起自己 。
2.释放独占资源
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
当 一个线程调用 re le ase( int arg)方法时会尝试使用 tryRele ase 操作释放资源,这里是设置状态变量 state 的值,然后调用 LockSupport.unpark(thread)方法激活 AQS 队列里面被阻塞的一个线程(thread) 。 被激活的线程则使用 tryAcquire 尝试,看当前状态变量 state的值是否能满足自己的需要,满足则该线程被激活,然后继续 向下运行,否则还是会被放入 AQS队列并被挂起。
3.入队操作
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//1:
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//2:
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
在上面例子说过,在获取资源的时候, 当 一个线程获取锁失败后该线程会被转换为 No de 节点,然后就会使用enq(final Node node) 方法将该节点插入到 AQS 的阻塞队列。下面结合图和代码来讲解入队操作

图I
初始状态下,tail和head都为null,代码会执行1处,tail和head会指向同一个节点。队列状态如图(I)所示

图II
当执行2处循环的时候,会通过cas算法判断是否能够正确获取到尾部节点,如果能够正确获取到的话,会按照图II所示步骤进行入队操作,如果不能正确获取到尾部节点,则再次执行循环体,知道能够正确获取到tail的值。
4.基于 AQS 实现自定义同步器
如下代码是基于 AQS 实现的独 占锁,对于原子变量 state 的含义,state 为 0 表示目前锁没有被线程持有 state 为 l 表示锁己经被某一个线程持有.
public class CustomerNonReentrantLock implements Lock {
/**
* 创建sync来做具体工作
*/
private final Sync sync = new Sync();
@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();
}
/**
* 内部帮助类
*/
private static class Sync extends AbstractQueuedSynchronizer {
/**
* 锁是否被持有
*
* @return true:
*/
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
@Override
protected boolean tryAcquire(int arg) {
assert arg == 1;
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
setState(1);
return true;
}
return false;
}
/**
* 释放锁
*
* @param arg 状态值
* @return true:成功
*/
@Override
protected boolean tryRelease(int arg) {
assert arg == 1;
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
final static CustomerNonReentrantLock lock = new CustomerNonReentrantLock();
final static Condition notFull = lock.newCondition();
final static Condition notEmpty = lock.newCondition();
final static Queue<String> queue = new LinkedBlockingQueue<String>();
final static int queueSize = 10;
/**
* 观察者消费者模式测试
*
* @param args args
*/
public static void main(String[] args) throws InterruptedException {
Thread produce = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
//队列满,等待
while (queue.size() == queueSize) {
notEmpty.await();
}
//添加队列元素
queue.add("ele");
System.out.println("生产者生产完成.");
//唤醒消费线程
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println("消费者获取锁");
try {
//队列为空,停止消费
while (queue.size() == 0) {
System.out.println("队列为空");
notFull.await();
}
//
String ele = queue.poll();
System.out.println("消费者消费完成.");
//唤醒生产线程
notEmpty.signalAll();
} catch (Exception e) {
e.printStackTrace();
;
} finally {
lock.unlock();
}
}
});
produce.start();
//Thread.sleep(20);
consumer.start();
}
}
此处为博主个人总结,可能能力有限,总结不当的,可留下评论,一起探讨。
AQS,即AbstractQueuedSynchronizer,是Java并发包中构建锁和其他同步组件的基础框架。本文详细解析AQS的工作原理,包括其内部类Node的属性与方法,以及AQS如何通过维护FIFO双向队列实现线程安全。同时,介绍了AQS的重要方法,如获取和释放独占资源,并展示了基于AQS实现自定义同步器的示例。
327

被折叠的 条评论
为什么被折叠?



