Java AQS无码讲解

Posted by ZY on November 11, 2018

很多人都听说过Java里有个叫AQS的东西,其实AQS只是Java里的一个类AbstractQueuedSynchronizer的缩写,当我们谈论AQS时,更多的是指那些利用AQS实现的同步工具类,比如SemaphoreCountDownLatchReentrantLock等。

那么什么是同步工具类呢?

打个比方,你开了一家饭馆,可以容纳10桌客人同时吃饭。来客人了,如果里头吃饭的不到10桌,那么请进,如果已经有10桌在吃了,那不好意思,请排队。

同样是排队,这一次,你不开饭馆了,你组织了一个饭局,邀请了10个人过来,人齐了才能开吃,这下排队的规则就变了,客人来了,看看到场的人到没到10个,到了,好,开吃,没到,不好意思,咱们继续聊聊天,玩玩手机,等那个该死的迟到的家伙。

排队的规则不尽相同,但也有相同的地方,那就是你总要安顿好排队的客人吧?当可以进来吃饭的时候,你需要取通知客人吧?

这些各种排队系统都需要的东西,比如安顿客人、通知客人用餐,就是AQS干的活,至于具体的排队规则,则是交给具体的同步工具类去制定。

现在让我们回到二进制世界中,现在,你要同步的,不再是人,而是线程。

你要安顿的不是客人,而是线程,你要把等待的线程,放到一个队列中去,然后把它们挂起,不让它们乱动,浪费CPU;然后,在合适的时候,唤醒这些线程。

二进制世界里,Semaphore就是上面说的饭馆,专业点的翻译,叫信号量,总共10个permits,进来一个线程,就拿走一个,拿完了,其他线程就不能进来,只能等待拿到permit的线程,释放permit,这时候其他线程才有机会进去。

CountDownLatch,没错,就是饭局,你设置了一个减数器,初始值为10,每来一个线程,减掉一个,减完还不等于0,那么线程等待,减到0时,ok,全部唤醒,放行。

SemaphoreCountDownLatchReentrantLock这些同步工具类,要做的,就只是写下自己的排队规则,所以看源码,你会发现这些类,注释比代码还多。

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

以上。

参考

  • Java Concurrency in Practice