同步类的基础AbstractQueuedSynchronizer(AQS)

我们之前介绍了很多同步类,比如ReentrantLock,Semaphore, CountDownLatch, ReentrantReadWriteLock,FutureTask等。

AQS封装了实现同步器时设计的大量细节问题。他提供了FIFO的wait queues并且提供了一个int型的state表示当前的状态。

根据JDK的说明,并不推荐我们直接使用AQS,我们通常需要构建一个内部类来继承AQS并按照需要重写下面几个方法:

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively

在这些方法中,我们需要调用getState, setState 或者 compareAndSetState这三种方法来改变state值。

上面的方法提到了两种操作,独占操作(如:ReentrantLock)和共享操作(如:Semaphore,CountdownLatch)。

两种的区别在于同一时刻能否有多个线程同时获取到同步状态。

比如我们运行同时多个线程去读,但是通知只允许一个线程去写,那么这里的读锁就是共享操作,而写锁就是独占操作。

在基于QAS构建的同步类中,最基本的操作就是获取操作和释放操作。而这个state就表示的是这些获取和释放操作所依赖的值。

State是一个int值,你可以使用它来表示任何状态,比如ReentrantLock用它来表示所有者线程重复获取该锁的次数。Semaphore用它来表示剩余的许可量,而FutureTask用它来表示任务的状态(开始,运行,完成或者取消)。当然你还可以自定义额外的状态变量来表示其他的信息。

下的伪代码表示的是AQS中获取和释放操作的形式:

   Acquire:
while (!tryAcquire(arg)) {
enqueue thread if it is not already queued;
possibly block current thread;
} Release:
if (tryRelease(arg))
unblock the first queued thread;

获取操作,首先判断当前状态是否允许获取操作,如果如果不允许,则将当前的线程入Queue,并且有可能阻塞当前线程。

释放操作,则先判断是否运行释放操作,如果允许,则解除queue中的thread,并运行。

我们看一个具体的实现:

public class AQSUsage {

    private final Sync sync= new Sync();

    private class Sync extends AbstractQueuedSynchronizer{
protected int tryAcquireShared(int ignored){
return (getState() ==1 )? 1: -1;
}
protected boolean tryReleaseShared(int ignored){
setState(1);
return true;
}
} public void release() {
sync.releaseShared(0);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(0);
}
}

上面的例子中,我们定义了一个内部类Sync,在这个类中我们实现了tryAcquireShared和tryReleaseShared两个方法,在这两个方法中我们判断并设置了state的值。

sync.releaseShared和sync.acquireSharedInterruptibly会分别调用tryAcquireShared和tryReleaseShared方法。

前面我们也提到了很多同步类都是使用AQS来实现的,我们可以再看看其他标准同步类中tryAcquire的实现。

首先看下ReentrantLock:

   final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

ReentrantLock只支持独占锁。所以它需要实现tryAcquire方法。除此之外它还维护了一个owner变量来保存当前所有者线程的标志符,从而来实现可重入锁。

我们再看下Semaphore和CountDownLatch的实现,因为他们是共享操作,所以需要实现tryAcqureShared方法:

        final int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}

本文的例子请参考https://github.com/ddean2009/learn-java-concurrency/tree/master/AQS

更多内容请访问 flydean的博客

同步类的基础AbstractQueuedSynchronizer(AQS)的更多相关文章

  1. 源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS)

    简介 AQS 全称是 AbstractQueuedSynchronizer,位于java.util.concurrent.locks 包下面,AQS 提供了一个基于FIFO的队列和维护了一个状态sta ...

  2. 全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(一)AQS基础

    AbstractQueuedSynchronizer(以下简称AQS)的内容确实有点多,博主考虑再三,还是决定把它拆成三期.原因有三,一是放入同一篇博客势必影响阅读体验,而是为了表达对这个伟大基础并发 ...

  3. 4.从AbstractQueuedSynchronizer(AQS)说起(3)——AQS结语

    前两节的内容<2.从AbstractQueuedSynchronizer(AQS)说起(1)——独占模式的锁获取与释放> .<3.从AbstractQueuedSynchronize ...

  4. AbstractQueuedSynchronizer AQS框架源码剖析

    一.引子 Java.util.concurrent包都是Doug Lea写的,来混个眼熟 是的,就是他,提出了JSR166(Java Specification RequestsJava 规范提案), ...

  5. 8.初识Lock与AbstractQueuedSynchronizer(AQS)

    1. concurrent包的结构层次 在针对并发编程中,Doug Lea大师为我们提供了大量实用,高性能的工具类,针对这些代码进行研究会让我们对并发编程的掌握更加透彻也会大大提升我们队并发编程技术的 ...

  6. 初识Lock与AbstractQueuedSynchronizer(AQS)

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  7. AbstractQueuedSynchronizer(AQS)抽丝剥茧深入了解JUC框架原理

    目录 简介 Lock简单实用 主体框架 原理解析 独占锁 AQS数据结构 CLH数据结构 acquire实现步骤 addWaiter acquireQueued shouldParkAfterFail ...

  8. java基础之AQS

    Java开发中,我们的应用程序经常会使用多线程提高程序的运行效率,多线程情况下访问线程共享变量可能会带来并发问题,此时就需要并发锁解决并发问题.Java提供了两种类型的并发控制机制:synchonri ...

  9. 全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(二)资源的获取和释放

    上期的<全网最详细的AbstractQueuedSynchronizer(AQS)源码剖析(一)AQS基础>中介绍了什么是AQS,以及AQS的基本结构.有了这些概念做铺垫之后,我们就可以正 ...

随机推荐

  1. Vertica的这些事(四)——-vertica加密数据

    通过创建 Secure Access Policies可以对vertica中的某一列数据进行加密: CREATE ACCESS POLICY ON [schema][tablename] FOR CO ...

  2. Java8 学习笔记--函数式接口与lambda表达式的关系

    在java中,lambda表达式与函数式接口是不可分割的,都是结合起来使用的. 对于函数式接口,我们可以理解为只有一个抽象方法的接口,除此之外它和别的接口相比并没有什么特殊的地方.为了确保函数式接口的 ...

  3. PTA | 1009说反话(20分)

    给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过80的字符串.字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有 ...

  4. 基于OAuth2.0的token无感知刷新

    目前手头的vue项目关于权限一块有一个需求,其实架构师很早就要求我做了,但是由于这个紧急程度不是很高,最近临近项目上线,我才想起,于是赶紧补上这个功能.这个项目是基于OAuth2.0认证,需要在每个请 ...

  5. flask入门 之 Python Shell (三)

    1.代码: #encoding:utf-8 from flask_sqlalchemy import SQLAlchemy from flask_script import Manager,Shell ...

  6. sparkRdd driver和excuter

    //1 从内存中创建makeRdd,底层实现就是parallelize val rdd=sc.makeRDD(Array(1,2,"df",55)) //2 从中创建paralle ...

  7. 小小的锁,大大的疑问?Lock疑问?

    Lock锁 怎么使用?怎么把下面的这个锁弄得比较合适,大家都能去买票?? 和synchronized相比的好处? lock的使用规范try finnally private final Reentra ...

  8. webWMS开发过程记录(六)- 详细设计之系统管理

    一.功能说明 1. 权限管理 (参考“权限管理-百度百科") 定义:一般指根据系统设置的安全规则或安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少. 分类:从控制力度来看,通常分 ...

  9. 【three.js第二课】页面自适应

    1.在[three.js第一课]的基础上加入以下代码,改变窗口大小时,页面内容会自适应 //加入事件监听器,窗口自适应 window.addEventListener('resize', functi ...

  10. Linux相关操作

    ssh配置秘钥 连接远程服务器时:需要用户持有“公钥/私钥对”,远程服务器持有公钥,本地持有私钥. 客户端向服务器发出请求.服务器收到请求之后,先在用户的主目录下找到该用户的公钥,然后对比用户发送过来 ...