初步理解AQS

AQS(AbstractQueuedSynchronizer)是Java并发包中重要的基础设施类,用于管理临界区。本文从FutureTask的实现出发,探讨AQS的设计思想和使用方式,包括其内部类Sync、层次结构、代码分析等,详细解析了AQS的线程控制、队列管理、节点状态和获取释放机制,帮助读者深入理解AQS的工作原理。
摘要由CSDN通过智能技术生成

欢迎移步google drive 阅读原文:

https://drive.google.com/file/d/0B758ryeeYYQYa1k2cHhzT0ZOSlU/edit?usp=sharing


AQS全称AbstractQueuedSynchronizer,它是concurrent包中最重要的基础设施类之一,负责作为模板类向业务层提供对临界区的管理。本文以FutureTask的实现机制作为引子,介绍了AQS的业务背景和设计思路,最后梳理了AQS的代码实现。

目录

摘要............................................................................................................................... 1

动机............................................................................................................................... 2

interrupt的真相.............................................................................................................................................................. 2

FUTURE TASK问题......................................................................................................................................................... 2

内部类Sync........................................................................................................................................................................... 4

层次结构....................................................................................................................... 4

业务层...................................................................................................................................................................................... 4

复用层...................................................................................................................................................................................... 7

代码分析....................................................................................................................... 8

如何复用AQS...................................................................................................................................................................... 8

【一个例子】.............................................................................................................................................................. 11

AQS内部细节................................................................................................................................................................... 15

概述.................................................................................................................................................................................... 15

线程控制......................................................................................................................................................................... 15

队列.................................................................................................................................................................................... 15

节点.................................................................................................................................................................................... 15

排他获取......................................................................................................................................................................... 17

排他释放......................................................................................................................................................................... 21

共享获取&释放.......................................................................................................................................................... 24

回到Future Task.......................................................................................................................................................... 29

 

 

interrupt的真相

一提起interrupt,大家往往想到的是:

1、其中文直译为“中断”

2、多线程编程中无处不在的InterruptedException

 

或许是受到了中文翻译的误导,我们不少人会以为调用Thread.interrupt()能中断任意线程。

 

其实,它只是表示有人要求中断,具体的实现是更新了一个boolean型的标志位(interrupt status)。至于是否响应中断以及何时中断,都是线程自己的事了。

 

这就好比老师在台上讲课,下面有学生想要提问,必须先举手(请求中断)。如果老师在写板书没看到(尚未轮询到中断标志位),则什么也不会发生;如果面对学生,那么老师一般会等到一句话说完,再允许该生发言(延迟响应中断)或者宣布“讲完这一节再统一提问”(忽略并重置中断)。

 

所以,调用Thread.interrupt()只是“举手”而已。

 

有关interrupt和InterruptedException的细节,请参考此文:http://blog.csdn.net/axman/article/details/431796

正如文中所说,“只有当线程执行到sleep,wait,join等方法时,或者自己检查中断标志而抛出异常的情况下,线程才会抛出InterruptedException。”

这样做的目的是允许线程安全可控地结束,避免盲目结束出现中间状态、资源没被释放等情况。

 

FUTURE TASK问题

接下来转到FutureTask。这是concurrent包里提供一个很方便的工具类,用于接受指定线程作为宿主,异步运行一个任务。其get()方法能阻塞当前用于查询结果的线程(以下简称查询线程)直到异步线程运行结束。

之前一度以为,若调用FutureTask.cancel()方法,能随时中止异步任务。

事实并非如此。就像中断机制一样,若异步任务本身没有通过Thread.isInterrupted()主动检查中断标志,那么它不仅不会结束,也不会抛出中断异常。倒是多个查询线程都能即时抛出CancellationException。

 

观察FutureTask.cancel()方法,其代码调用了内部类Sync.innnerCancel(),后者看起来只是简单地触发了Thread.interrupt(),这符合我们的预期。但又是怎样的机制分别唤醒了处于阻塞状态的查询线程呢?代码中releaseShared(0)引起了我的注意。

 

 

       boolean innerCancel(boolean mayInterruptIfRunning) {

            for(;;) {

                int s= getState();

                if(ranOrCancelled(s))

                    return false;

                if(compareAndSetState(s,CANCELLED))

                    break;

            }

            if(mayInterruptIfRunning) {

                Thread r = runner;

                if(r != null)

                    r.interrupt();

            }

            releaseShared(0);

            done();

            return true;

        }

为了搞清楚这个问题的来龙去脉,我展开了对FutureTask内部类Sync的调查,于是自然延伸到了其基类AbstractQueuedSynchronizer(以下简称AQS)。AQS是如此庞杂,绝非三言两语能说得清楚;更糟糕的是,网上大量有关该类的文章和学习笔记,要么蜻蜓点水一笔带过,要么一上来就跳入了代码细节的深渊,为此我绕了一个大圈才理顺。当时多么希望有一篇文章能深入浅出,用先宏观再微观的梳理方式,把这个类讲讲清楚。现在我也来写一篇AQS的学习笔记,以飨小伙伴们。

 

 

先考虑如下现实世界中的情况:


(图片引用自http://www.jq1997.cn/mkldfffiles/2012531105117204.jpg)

 

l   例如,男厕是临界区,有些高端大气的小便槽(上图)可供一起使用(共享访问),容纳人数有限(资源有限);保洁员可以”在入口处放一块牌子“清洁中”(排他访问),也可以同时清洁。

l   又如,游泳池是临界区,泳客是共享访问,一场结束的时候倒消毒液的人“必须”先清场,再操作(排他访问)

l   再如,停车场是临界区,车辆均共享访问,车位(资源)有限,且小车占一个车位,大巴占两个车位。

 

可见,上述业务经常变化的部分是对临界区的资源管理,主要包含:

l   临界区内不同的“资源总数”;

l   每次访问占用的“资源数量”;

l   支持两种不同的“访问方式”(排他/共享)。

 

至于临界区被占满后,如何排队、能否插队(此处特指跳过排队直接进入临界区,注1)等管理细节都是雷同的。

 

接下来回到java的世界

让我们先来看看concurrent包里常见的并发控制相关的业务类。

 

类名

功能

资源总数

如何访问

备注

Mutex

互斥锁

1

仅排他

出现在AQS类的注释中

Semaphore

信号量

可指定(构造参数permits)

仅共享

 

ReentrantReadWriteLock

读写(可重入)锁

无上限(实际为16位unsigned short)

读锁共享访问,写锁排他访问

关于“可重入”见注2;

 

注意到,表内与前述资源管理过程中

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值