Java AQS学习

Posted 咸鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java AQS学习相关的知识,希望对你有一定的参考价值。

AQS 作用

AQS简介

AQS是AbstractQueuedSynchronizer的缩写,AQS是用来构建锁或者其他同步组件的基础框架(或者说是基础工具类),它维护了一个volatile int state来表示同步状态和一个FIFO队列来完成资源获取过程中阻塞排队的工作,并发包作者(Doug Lea)期望它能成为实现大部分同步需求的基础。

AQS的使用

AQS的主要使用方式是继承,子类通过继承AQS并实现它的抽象方法来管理同步状态;子类推荐被定义为自定义同步组件的静态内部类。子类通过继承同步器并实现AQS的抽象方法来管理同步状态,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的工具方法来供自定义同步组件使用。AQS采用了CAS的方式保证了同步状态的改变是线程安全的。state状态访问的三种方法:

  • getState()
  • setState()
  • compareAndSetState()

AQS定义两种资源共享方式:Exclusive(独占,同一时刻只有一个线程能执行,如ReentrantLock)和Share(共享式,多个线程可同时进行访问,如Semaphore/CountDownLatch)。

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时需要实现共享资源state的获取和释放方式即可,至于具体实现等待队列的维护(如获取资源失败入队\唤醒队列等),AQS在底层已经实现好了。自定义同步器实现时主要需要实现以下几种方法:

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

以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcqure()独占该锁并将state+1。以后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其他线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state累加),这就是可重入的概念。但要注意,获取多少次就要释放多少次,这样才能保证state是能回到0态的。

再以CountDownLatch为例,任务分为N个线程进行执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()住调用线程主调用线程,然后主调用线程就会从await()函数中返回,继续后余动作。

一般来说,自定义同步器要么是独占式,要么是共享式,他们也只需要实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如:ReentrantReadWriteLock。

同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。

以上是关于Java AQS学习的主要内容,如果未能解决你的问题,请参考以下文章

Java高并发学习——AQS及源码解析

Java并发之AQS同步器学习

AQS源码学习

java1.8 AQS AbstractQueuedSynchronizer学习

Java锁原理学习

死磕 java同步系列之AQS终篇(面试)