java中的AQS

在java中论锁,必定要看 AbstractQueuedSynchronizer ,之前学习过的ReentrantLock,CountDownLatch等等都是基于它来实现的。

概述

AbstractQueuedSynchronizer字面含义抽象的队列同步器,简称AQS,AQS定义了一套多线程访问共享资源的同步器框架,使得基于此可实现多线程同步,这也是java中继synchronized关键字后目前应用最多的同步框架,基于他的实现有CountDownLatchReentrantLockReentrantReadWriteLockSemaphoreLimitLatchThreadPoolExecutor.Worker,简单说就是java并发包的基石。

网上很经典的一张图整个 AQS 内部就是维护的一个FIFO(first input,first out)队列。通过 CAS(compareAndSwap) 最终实现原子操作 state 实现状态变换。

AQS支持两种资源共享模式:

  • Exclusive 独占模式,实现如 ReentrantLock .
  • Share 共享模式,如 ReentrantReadWriteLock/Semaphore 等 .

不同的自定义同步器争用共享资源的方式也不相同,基于AQS框架实现的自定义同步器只需要实现控制实现共享资源state的获取与释放即可,至于具体线程排队队列等的维护,框架已经维护好了。如果需要自定义一个同步器,主要要实现以下几个方法:

  • tryAcquire 尝试独占方式获取锁
  • tryRelease 尝试释放独占锁
  • tryAcquireShared 尝试获取共享锁
  • tryReleaseShared 尝试释放共享锁
  • isHeldExclusively 当前线程是否正在独占资源,只有用到 Condition 时才需要实现。

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

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

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

本章多数内容来自 :https://www.cnblogs.com/waterystone/p/4920797.html
写的很好,推荐结合源码学习。