业务范围
联系我们

地址:这里是您的公司地址

电话:0896-98589990

传真:0896-98589990

邮箱:

新闻资讯当前位置:官网首页 > 新闻资讯 >
Java 并发之 AQS 详解

发布时间:2019-09-04

  

作者:水岩

www.cnblogs.com/waterystone/p/4920797.html


一、概述


谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer!


类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...。


以下是本文的目录大纲:


概述

框架

源码详解

简单应用


二、框架



它维护了一个volatile int state和一个FIFO线程等待队列。这里volatile是核心关键词,具体volatile的语义,在此不述。state的访问方式有三种:


getState

setState

compareAndSetState


AQS定义两种资源共享方式:Exclusive和Share。



不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护,AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:


isHeldExclusively:该线程是否正在独占资源。只有用到condition才需要去实现它。

tryAcquire:独占方式。尝试获取资源,成功则返回true,失败则返回false。

tryRelease:独占方式。尝试释放资源,成功则返回true,失败则返回false。

tryAcquireShared:共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

tryReleaseShared:共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。


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



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



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


三、源码详解


本节开始讲解AQS的源码实现。依照acquire-release、acquireShared-releaseShared的次序来。


3.1 acquire


此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。这也正是lock的语义,当然不仅仅只限于lock。获取到资源后,线程就可以去执行其临界区代码了。下面是acquire的源码:



public final void acquire {
if &&
acquireQueued, arg))
selfInterrupt;
}





函数流程如下:


tryAcquire尝试直接去获取资源,如果成功则直接返回;

addWaiter将该线程加入等待队列的尾部,并标记为独占模式;

acquireQueued使线程在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。

如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt,将中断补上。


这时单凭这4个抽象的函数来看流程还有点朦胧,不要紧,看完接下来的分析后,你就会明白了。就像《大话西游》里唐僧说的:等你明白了舍生取义的道理,你自然会回来和我唱这首歌的。


3.1.1 tryAcquire


此方法尝试去获取独占资源。如果获取成功,则直接返回true,否则直接返回false。这也正是tryLock的语义,还是那句话,当然不仅仅只限于tryLock。如下是tryAcquire的源码:



protected boolean tryAcquire

地址: 电话:
Copyright © 2018 k8凯发国际娱乐k8凯发国际娱乐-k8凯发国际娱乐下载 All Rights Reserved