![1cf6004d77cfdaf98203b35f058cf97d.png](https://i-blog.csdnimg.cn/blog_migrate/a2aa63126e7cabc82c7bce2c7590552a.jpeg)
上一篇已经实现了一个阻塞队列,阻塞队列的关键是则是线程,那么并发库中的类是如何阻塞线程的呢?
阻塞队列的再次优化
在上一篇文章《多线程并发基础通过Object的方法实现阻塞队列》中通过层层优化,最终采用Object的wait()与notifyAll()就实现了一个高响应并且不浪费CPU的阻塞队列。
在Java的java.util.concurrent也提供了一些类能够实现这个功能,在之前学习的Lock接口时讲了他提供的几个主要方法作为获取锁和释放锁的基本功能,但是最后一个方法“Condition newCondition()”并没有详细去了解,而它也是实现阻塞队列的关键,直接看代码:
![3eac4cd841aeaa4cbbbd12e7898b9607.png](https://i-blog.csdnimg.cn/blog_migrate/9df737d021352c1d65a6b52c1dfba685.jpeg)
通过两个Condition对象notFull、notEmpty分别控制控制put与take方法的阻塞,同时put或take方法执行成功又会通知notEmpty或notFull可以继续执行,这种方式的实现比wait()与notifyAll()方式更加复杂,但是他们的精细度更高,notifyAll()会唤醒所有等待中的线程去竞争同一个锁,put方法成功还是有可能让另外一个put方法获取到锁,而Condition方式则只会唤醒需要唤醒的线程,减少了锁竞争。
Condition的实现与内置的wait()与notifyAll()对比就好像Lock与synchronized的对比,实现方案可能更加复杂,不过却更加的灵活并且粒度更细,实现更加自主可能提高更好的性能。
通过对缓存队列的实现可以看出除了对数据的同步控制外,还有就是要在合适的地方对阻塞线程和唤醒线程,当队列为空时阻塞take线程,当队列满了的时候阻塞put线程,take成功后队列不在处于饱满状态要唤醒put线程,put成功后队列肯定不在是空队列所以要唤醒take线程。所有的阻塞与唤醒都是基于对队列状态的判断。
AbstractQueuedSynchronizer
那么再来看Java中原生的一些并发类,比如Lock的lock方法获取锁也会阻塞,而unlock方法会通知所有lock阻塞线程再次竞争锁。信号量Semaphore的acquire()方法获取许可如果没有获取到就阻塞,release()方法释放获取的许可,其他正在阻塞的就可以获取到然后继续执行,闭锁CountDownLatch的await()可以阻塞线程,countDown()方法执行一次就减少预设的次数,当设置的值被减到0时所有await阻塞的线程开始执行。
与我们实现的阻塞队列一样,都是一个方法在阻塞线程,然后等待另外一个方法来释放信号,这些类说到底还是竞争资源,那么他们竞争的是什么资源呢?通过源码可以得出ReentrantLock、Semaphore、CountDownLatch内部都维护这一个抽象类AbstractQueuedSynchronizer的继承类。
AbstractQueuedSynchronizer中维护着一个int型的state,这个state就像我们实现的阻塞队列中的队列状态一样是所谓的资源,不同的操作对应state的不同处理。
几个阻塞线程类的实现
ReentrantLock的lock方法实际上是调用compareAndSetState(int expect, int update)方法尝试把state从0设置为1,unlock则是把1改回0,ReentrantLock中还维护一个exclusiveOwnerThread用来保存拥有锁的线程,在lock的时候设置这个值,unlock时把这个值设为null。
Semaphore的处理方法,在构造方法“public Semaphore(int permits)”中把state的值初始化为permits的值,acquire()方法会通过compareAndSetState(int expect, int update)减少state的值,会优先计算减少后的值,如果小于0会进入等待队列中等待。而release()方法则会增加state值。
CountDownLatch和信号量有点相似,先初始化state一个值,不同的是后面的处理,countDown()会更新state减少这个值直到0,而await则是去判断这个值是否这个值是否等于0,不等于0就阻塞。
总结
通过利用再次对阻塞队列的优化,知道了如何优美的阻塞线程和再次启动,通过判断队列状态并利用Lock来实现阻塞与唤醒,那么Lock又是如何阻塞和唤醒线程的呢?通过对lock方法和unlock方法源码可以看到它维护这一个AbstractQueuedSynchronizer的子类,通过对AbstractQueuedSynchronizer的state状态来判断是否阻塞,通过compareAndSetState方法和AbstractQueuedSynchronizer的阻塞队列来实现线程的阻塞,这两块并没有深入,等深入后再来解惑吧。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
![13be87952c9f90f4f4e9e1139d48a147.png](https://i-blog.csdnimg.cn/blog_migrate/e2f7aa4d2cc563ef43f588a0f72da323.jpeg)