很多人都听说过 Java 里有个叫 AQS 的东西, 其实 AQS 只是 Java 里的一个类 AbstractQueuedSynchronizer 的缩写, 当我们谈论 AQS 时, 更多的是指那些利用 AQS 实现的同步工具类, 比如 Semaphore,CountDownLatch,ReentrantLock 等.
那么什么是同步工具类呢?
打个比方, 你开了一家饭馆, 可以容纳 10 桌客人同时吃饭. 来客人了, 如果里头吃饭的不到 10 桌, 那么请进, 如果已经有 10 桌在吃了, 那不好意思, 请排队.
同样是排队, 这一次, 你不开饭馆了, 你组织了一个饭局, 邀请了 10 个人过来, 人齐了才能开吃, 这下排队的规则就变了, 客人来了, 看看到场的人到没到 10 个, 到了, 好, 开吃, 没到, 不好意思, 咱们继续聊聊天, 玩玩手机, 等那个该死的迟到的家伙.
排队的规则不尽相同, 但也有相同的地方, 那就是你总要安顿好排队的客人吧? 当可以进来吃饭的时候, 你需要取通知客人吧?
这些各种排队系统都需要的东西, 比如安顿客人, 通知客人用餐, 就是 AQS 干的活, 至于具体的排队规则, 则是交给具体的同步工具类去制定.
现在让我们回到二进制世界中, 现在, 你要同步的, 不再是人, 而是线程.
你要安顿的不是客人, 而是线程, 你要把等待的线程, 放到一个队列中去, 然后把它们挂起, 不让它们乱动, 浪费 CPU; 然后, 在合适的时候, 唤醒这些线程.
二进制世界里, Semaphore 就是上面说的饭馆, 专业点的翻译, 叫信号量, 总共 10 个 permits, 进来一个线程, 就拿走一个, 拿完了, 其他线程就不能进来, 只能等待拿到 permit 的线程, 释放 permit, 这时候其他线程才有机会进去.
而 CountDownLatch, 没错, 就是饭局, 你设置了一个减数器, 初始值为 10, 每来一个线程, 减掉一个, 减完还不等于 0, 那么线程等待, 减到 0 时, ok, 全部唤醒, 放行.
Semaphore,CountDownLatch,ReentrantLock 这些同步工具类, 要做的, 就只是写下自己的排队规则, 所以看源码, 你会发现这些类, 注释比代码还多.
Talk is cheap. Show me the code?
Sorry, 这是一篇无码的文章.
了解 AQS 的原理, 主要是为了更好的弄懂我们经常使用的这些同步工具类的运行机制, 出了问题, 好知道原因.
这里稍微分享下 AQS 源码里的几个关键词吧:
组合: Effective Java 里提到, 组合是比继承更好的复用代码的方式, Java 源码里通过组合的方式来复用 AQS 的, 具体如何组合, 看看 CountDownLatch 即可
state 状态: 这是
AbstractQueuedSynchronizer
里一个万能的属性, 具体是什么含义, 全看你的使用方式, 比如在 CountDownLatch 里, 它代表了当前到达后正在等待的线程数, 在 Semaphore 里, 它则表示当前进去后正在运行的线程数
CAS: AQS 里大量用了 CAS 操作来修改 state 的值
LockSupport: AQS 里用了大量的 LockSupport 的 park() 和 unpark() 方法, 来挂起和唤醒线程
同步队列和条件队列: sync queue and condition queue, 弄清楚这两个队列的关系, AQS 也就弄懂大半
公平和非公平: 有线程竞争, 就有公平和非公平的问题. 锁释放的时候, 刚好有个线程过来获取锁, 但这时候线程等待队列里也有线程在等待, 到底是给排队时间最久的线程呢 (公平), 还是允许新来的线程参与竞争 (不公平)?
这篇文章只是一个引子, 如果你想深入学习 AQS, 可以自己写几个 demo, 然后调试看看, 也可以看看我们组一位大佬写的文章: 天外流星 for
以上.
来源: http://www.jianshu.com/p/c27d3c351d48