AQS的设计思想是通过继承的方式提供一个模板让大家能够非常easy依据不同场景实现一个富有个性化的同步器。同步器的核心是要管理一个共享状态,通过对状态的控制即能够实现不同的锁机制。

AQS的设计必须考虑把复杂反复且easy出错的队列管理工作统一抽象出来管理,而且要统一控制好流程,而暴露给子类调用的方法主要就是操作共享状态的方法,以此提供对状态的原子性操作。一般子类的同步器中使用AQS提供的getState、setState、compareAndSetState三个方法,前两个为普通的get和set方法,要使用这两个方法必需要保证不存在数据竞争,compareAndSetState方法提供了CAS方式的硬件级别的原子更新。

对于独占模式而言。锁获取与释放的流程的定义则交给acquire和release两个方法,它们定义了锁获取与释放的逻辑。同一时候也是提供给子类获取和释放锁的接口。它的运行逻辑能够參考前面的“锁的获取与释放”。它提供了一个如何强大的模板?由以下的伪代码能够清晰展示出来。请注意tryAcquire和tryRelease这两个方法,它就是留给子类实现个性化的方法,通过这两个方法对共享状态的管理能够自己定义多种多样的同步器。而队列的管理及流程的控制则不是你需要考虑的问题。

① 锁获取模板

if(tryAcquire(arg)) {

    创建node

    使用CAS方式把node插入到队列尾部

    while(true){

    if(tryAcquire(arg) 而且 node的前驱节点为头节点){

把当前节点设置为头节点

    跳出循环

}else{

    使用CAS方式改动node前驱节点的waitStatus标识为signal

    if(改动成功)

        挂起当前线程 

}

}

② 锁释放模板

    if(tryRelease(arg)){

唤醒兴许节点包括的线程

}





我们能够觉得同步器可实现不论什么不同锁的语义。一般提供给使用者的锁是用AQS框架封装实现的更高层次的实现,提供一种更加形象的API让使用者使用起来更加方便简洁。而不是让使用者直接接触AQS框架,比如。ReentrantLock、Semphore、CountDownLatch等等。这些不同的形象的锁让你使用起来更好理解更加得心应手,并且不easy混淆。

然而这些锁都是由AQS实现。AQS同步器面向的是线程和状态的控制,定义了线程获取状态的机制及线程排队等操作,非常好地隔离了两者的关注点,高层关注的是场景的使用,而AQS同步器则关心的是并发的控制。

假如你要实现一个自己定义同步装置,官方推荐的做法是将集成AQS同步器的子类作为同步装置的内部类,而同步装置中相关的操作仅仅需代理成子类中相应的方法就可以。往下用一个简单的样例看看怎样实现自己的锁,因为同步器被分为两种模式。独占模式和共享模式。所以样例也相应给出。





① 独占模式。独占模式採取的样例是银行服务窗体,假如某个银行网点仅仅有一个服务窗体,那么此银行服务窗体仅仅能同一时候服务一个人。其它人必须排队等待,所以这样的银行窗体同步装置是一个独占模型。

第一个类是银行窗体同步装置类。它依照推荐的做法使用一个继承AQS同步器的子类实现,并作为子类出现。第二个类是測试类,形象一点地说。有三位良民到银行去办理业务,各自是tom、jim和jay,我们使用BankServiceWindow就能够约束他们排队,一个一个轮着办理业务而避免陷入混乱的局面。

public class BankServiceWindow {

private final Sync sync;

public BankServiceWindow() {

sync = new Sync();

}

private static class Sync extends AbstractQueuedSynchronizer {

public boolean tryAcquire(int acquires) {

if (compareAndSetState(0, 1)) {

setExclusiveOwnerThread(Thread.currentThread());

return true;

}

return false;

}

protected boolean tryRelease(int releases) {

if (getState() == 0)

throw new IllegalMonitorStateException();

setExclusiveOwnerThread(null);

setState(0);

return true;

}

}

public void handle() {

sync.acquire(1);

}

public void unhandle() {

sync.release(1);

}

}





public class BankServiceWindowTest {

   public static void main(String[] args){

   final BankServiceWindow bankServiceWindow=new BankServiceWindow();

   Thread tom=new Thread(){

   public void run(){

   bankServiceWindow.handle();

   System.out.println("tom開始办理业务");

   try {

   this.sleep(5000);

   } catch (InterruptedException e) {

   e.printStackTrace();

   }

   System.out.println("tom结束办理业务");

   bankServiceWindow.unhandle();

   }

   };

   Thread jim=new Thread(){

   public void run(){

   bankServiceWindow.handle();

   System.out.println("jim開始办理业务");

   try {

   this.sleep(5000);

   } catch (InterruptedException e) {

   e.printStackTrace();

   }

   System.out.println("jim结束办理业务");

   bankServiceWindow.unhandle();

   }

   };

   Thread jay=new Thread(){

   public void run(){

   bankServiceWindow.handle();

   System.out.println("jay開始办理业务");

   try {

   this.sleep(5000);

   } catch (InterruptedException e) {

   e.printStackTrace();

   }

   System.out.println("jay结束办理业务");

   bankServiceWindow.unhandle();

   }

   };

   tom.start();

   jim.start();

   jay.start();

    }

}

输出结果例如以下:

tom開始办理业务

tom结束办理业务

jim開始办理业务

jim结束办理业务

jay開始办理业务

jay结束办理业务

明显tom、jim、jay仨是排队完毕的。可是无法保证三者的顺序,可能是tom、jim、jay,也可能是tom、jay、jim。由于在入列曾经的运行先后是无法确定的,它的语义是保证一个接一个办理。

假设没有同步器限制的情况。输出结果将不可预測。可能为输出例如以下:

jim開始办理业务

jay開始办理业务

tom開始办理业务

jay结束办理业务

jim结束办理业务

tom结束办理业务





② 共享模式,共享模式採取的样例相同是银行服务窗体,随着此网点的发展。办理业务的人越来越多,一个服务窗体已经无法满足需求,于是又分配了一位员工开了另外一个服务窗体,这时就能够同一时候服务两个人了,但两个窗体都有人占用时相同也必须排队等待,这样的服务窗体同步器装置就是一个共享型。第一个类是共享模式的同步装置类,跟独占模式不同的是它的状态的初始值能够自由定义,获取与释放就是对状态递减和累加操作。第二个类是測试类,tom、jim和jay再次来到银行,一个有两个窗体甚是高兴,他们能够两个人同一时候办理了,时间缩减了不少。

public class BankServiceWindows {

private final Sync sync;

public BankServiceWindows(int count) {

sync = new Sync(count);

}

private static class Sync extends AbstractQueuedSynchronizer {

Sync(int count) {

setState(count);

}

public int tryAcquireShared(int interval) {

for (;;) {

int current = getState();

int newCount = current - 1;

if (newCount < 0 || compareAndSetState(current, newCount)) {

return newCount;

}

}

}

public boolean tryReleaseShared(int interval) {

for (;;) {

int current = getState();

int newCount = current + 1;

if (compareAndSetState(current, newCount)) {

return true;

}

}

}

}





public void handle() {

sync.acquireShared(1);

}





public void unhandle() {

sync.releaseShared(1);

}





}





public class BankServiceWindowsTest {

public static void main(String[] args){

final BankServiceWindows bankServiceWindows=new BankServiceWindows(2);

Thread tom=new Thread(){

public void run(){

bankServiceWindows.handle();

System.out.println("tom開始办理业务");

try {

this.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("tom结束办理业务");

bankServiceWindows.unhandle();

}

};

Thread jim=new Thread(){

public void run(){

bankServiceWindows.handle();

System.out.println("jim開始办理业务");

try {

this.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("jim结束办理业务");

bankServiceWindows.unhandle();

}

};

Thread jay=new Thread(){

public void run(){

bankServiceWindows.handle();

System.out.println("jay開始办理业务");

try {

this.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("jay结束办理业务");

bankServiceWindows.unhandle();

}

};

tom.start();

jim.start();

jay.start();

}

}

可能的输出结果为:

tom開始办理业务

jay開始办理业务

jay结束办理业务

tom结束办理业务

jim開始办理业务

jim结束办理业务

tom和jay差点儿同一时候開始办理业务,而jay结束后有空暇的服务窗体jim才过来。

这节主要讲的是怎样使用AQS构建自己的同步器。而且剖析了锁获取与释放的模板的逻辑,让你更好理解AQS的实现,最后分别给出了独占模式和共享模式的同步器实现的样例。相信你们搞清楚这两种方式的实现后。要构建更加复杂的同步器就知道力往哪里使了。

喜欢研究java的同学能够交个朋友。以下是本人的微信号:

Java并发框架——AQS之怎样使用AQS构建同步器的更多相关文章

  1. Java并发框架AbstractQueuedSynchronizer(AQS)

    1.前言 本文介绍一下Java并发框架AQS,这是大神Doug Lea在JDK5的时候设计的一个抽象类,主要用于并发方面,功能强大.在新增的并发包中,很多工具类都能看到这个的影子,比如:CountDo ...

  2. 深入理解Java并发框架AQS系列(一):线程

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...

  3. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

  4. 深入理解Java并发框架AQS系列(四):共享锁(Shared Lock)

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock) 深入 ...

  5. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  6. java并发编程笔记(六)——AQS

    java并发编程笔记(六)--AQS 使用了Node实现FIFO(first in first out)队列,可以用于构建锁或者其他同步装置的基础框架 利用了一个int类型表示状态 使用方法是继承 子 ...

  7. 【死磕Java并发】-----J.U.C之AQS:CLH同步队列

    此篇博客全部源代码均来自JDK 1.8 在上篇博客[死磕Java并发]-–J.U.C之AQS:AQS简单介绍中提到了AQS内部维护着一个FIFO队列,该队列就是CLH同步队列. CLH同步队列是一个F ...

  8. Java 并发系列之十:java 并发框架(2个)

    1. Fork/Join框架 2. Executor框架 3. ThreadPoolExecutor 4. ScheduledThreadPoolExecutor 5. FutureTask 6. t ...

  9. 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)

    一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...

  10. 【死磕Java并发】—–J.U.C之AQS(一篇就够了)

    [隐藏目录] 1 独占式 1.1 独占式同步状态获取 1.2 独占式获取响应中断 1.3 独占式超时获取 1.4 独占式同步状态释放 2 共享式 2.1 共享式同步状态获取 2.2 共享式同步状态释放 ...

随机推荐

  1. Ubuntu 下安装adobe reader

    ctrl+alt+t打开终端 wget ftp://ftp.adobe.com/pub/adobe/reader/unix/9.x/9.5.5/enu/AdbeRdr9.5.5-1_i386linux ...

  2. 大并发server架构 &amp;&amp; 大型站点架构演变

    server的三条要求: 高性能:对于大量请求,及时高速的响应 高可用:7*24 不间断,出现问题自己主动转移.这叫fail over(故障转移) 伸缩性:使用跨机器的通信(TCP) 另外不论什么网络 ...

  3. C++ 代码风格准则:POD

    作者:一根筋的傻瓜链接:https://www.zhihu.com/question/36379130/answer/69853366来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

  4. 【VBA编程】14.操作工作簿对象

    [访问工作簿] 对已经打开的工作簿,可以通过使用索引号来访问工作簿,也可以通过名称来访问工作簿 [代码区域] Sub 访问工作簿() Dim counter As Integer counter = ...

  5. Nutch的发展历程(转)

    2002年8月由Doug Cutting发起,托管于Sourceforge,之后发布了0.4.0.5.0.6三个版本 2004年9月Oregon State University(俄勒冈州立大学)采用 ...

  6. :not() 选择器选取除了指定元素以外的所有元素

    :not() 选择器选取除了指定元素以外的所有元素; 最近有人那上图问:如果触碰li时背景色和字体都变化,但是已经被选中的(现在是第一行)不变怎么办?:not是一个非常简单方便的办法:加入我给已经被选 ...

  7. 体验了把做HR的感觉,上午看了40份简历,说说感受

    原文链接:http://huachichi.info/2013/06/26/experience-of-being-a-hr/   这两天准备从IBM离职,不要问我为什么要在这么bug的时间离职,总之 ...

  8. Linux top命令的图解使用

    Linux下的top命令的图解使用     linux下top命令参数解释     TOP命令详解

  9. HR问“预期薪资是多少”,这么说能加薪zz

    每年过完节,收好上一年的年终奖,身边人就开始蠢蠢欲动,招聘市场也异常火爆,节前各种裁员的新闻,过了个节都变成了“我们还要继续招人”. 年景不好,人才更是成了紧俏货.可现实中,我却发现,优质的人才未必能 ...

  10. [svc][jk][mem]linux 内存清理/释放命令

    1.清理前内存使用情况 free -m 2.开始清理  echo 1 > /proc/sys/vm/drop_caches 3.清理后内存使用情况 free -m 4.完成! 查看内存条数命令: ...