谈到阻塞,相信你们都不会陌生了。阻塞的应用场景真的多得不要不要的,好比 生产-消费模式,限流统计等等。什么 ArrayBlockingQueue、 LinkedBlockingQueue、DelayQueue 等等,都是阻塞队列的实现啊,多简单!java
阻塞,通常有两个特性很亮眼:1. 不耗 CPU 等待;2. 线程安全;node
额,要这么说也 OK 的。毕竟,咱们遇到的问题,到这里就够解决了。可是有没有想过,这容器的阻塞又是如何实现的呢?linux
好吧,翻开源码,也很简单了:(好比 ArrayBlockingQueue 的 take、put….)安全
1架构 2app 3less 4分布式 5ide 6工具 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
看来,最终都是依赖了 AbstractQueuedSynchronizer 类(著名的AQS)的 await 方法,看起来像那么回事。那么这个同步器的阻塞又是如何实现的呢?
Java的代码老是好跟踪的:
// AbstractQueuedSynchronizer.await()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
如上,能够看到,真正的阻塞工做又转交给了另外一个工具类: LockSupport 的 park 方法了,这回跟锁扯上了关系,看起来已经愈来愈接近事实了:
// LockSupport.park()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
看得出来,这里的实现就比较简洁了,先获取当前线程,设置阻塞对象,阻塞,而后解除阻塞。
好吧,到底什么是真正的阻塞,咱们仍是不得而知!
UNSAFE.park(false, 0L); 是个什么东西? 看起来就是这一句起到了最关键的做用呢!但因为这里已是 native 代码,咱们已经没法再简单的查看源码了!那咋整呢?
那不行就看C/C++的源码呗,看一下 parker 的定义(park.hpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
那 park() 方法究竟是如何实现的呢? 实际上是继承的 os::PlatformParker 的功能,也就是平台相关的私有实现,以 Linux 平台实现为例(os_linux.hpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
看到 park.cpp 中没有重写 park() 和 unpark() 方法,也就是说阻塞实现彻底交由特定平台代码处理了(os_linux.cpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
|
从上面代码能够看出,阻塞主要借助于三个变量,_cond、_mutex、_counter, 调用 Linux 系统的 pthread_cond_wait、pthread_mutex_lock、pthread_mutex_unlock (一组 POSIX 标准的阻塞接口)等平台相关的方法进行阻塞了!
而 park.cpp 中,则只有 Allocate、Release 等的一些常规操做!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
综上源码,在进行阻塞的时候,底层并无(并不必定)要用 while 死循环来阻塞,更多的是借助于操做系统的实现来进行阻塞的。固然,这也更符合你们的猜测!
从上的代码咱们也发现一点,底层在作许多事的时候,都不忘考虑线程中断,也就是说,即便在阻塞状态也是能够接收中断信号的,这为上层语言打开了方便之门。
若是要细说阻塞,其实还远没完,不过再往操做系统层面如何实现,就得再下点功夫,去翻翻资料了,把底线压在操做系统层面,大多数状况下也够用了!
欢迎学Java和大数据的朋友们加入java架构交流: 855835163
加群连接:https://jq.qq.com/?_wv=1027&k=5dPqXGI
群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深刻浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解 能够进来一块儿学习交流哦