J.U.C之AQS

double

记录一下

什么是AQS?

AbstractQueuedSynchronizer(AQS 也叫队列同步器,这名字应该是因为其内部是通过队列实现同步状态管理、线程排队、等待与唤醒的),该类位于 java.utils.concurrent.locks 包,它是构建锁获取其他同步组件的基础框架,如 Semophore、ReentrantLock、ReentrantReadWriteLock。AQS解决了子类实现同步器时涉及到的大量细节问题,例如获取同步状态、FIFO同步队列。它不仅能够极大减少实现工作,而且也不必处理在多个位子上发生的竞争问题。在基于AQS构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提供吞吐量。

底层实现主要有以下:

  • 核心数据结构: 双向链表 + state(锁状态)
  • 底层操作: CAS
  • 设计模式: 模板模式

AQS定义两种资源共享方式:

  • Exclusive(独占,只有一个线程能执行,如 ReentrantLock)
  • Share(共享,多个线程可共同执行,如 Semaphore/CountDownLatch)

AQS实现
AQS的主要实现方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。

相关api:

  • isHeldExclusively(): 该线程是否正在独占资源,只有用到 condition 才需要去实现它
  • tryAcquire(int): 独占方式,尝试获取资源,成功返回 true, 失败返回 false
  • tryRelease(int): 独占方式,尝试释放资源,成功返回 true, 失败返回 false
  • tryAcquireShared(int): 共享方式,尝试获取资源,负数表示失败;0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
  • tryReleaseShared(int): 共享方式,尝试释放资源,如果释放后允许唤醒后继续等待节点返回 true, 否则返回false

示例问题

public class Ttt { public static void main(String[] args) { final int[] counter = {0}; ReentrantLock lock = new ReentrantLock(); for( int i = 0; i < 50; i++) { new Thread(() -> { System.err.println(Thread.currentThread().getName() + ":lock之前"); lock.lock(); try { int a = counter[0]; counter[0] = a + 1; }finally { // 保证能完全释放锁 lock.unlock(); } }).start(); /** * 主线程休眠,等待结果 * TODO: 这里主线程怎么会一直执行 * * (深入理解 Java 之 Sync 到底锁住了啥)[https://www.jfh.com/jfperiodical/article/4653] */ try { TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + ":" + counter[0]); } catch (InterruptedException e) { e.printStackTrace(); } } } }

参考文章