談到阻塞,相信你們都不會陌生了。阻塞的應用場景真的多得不要不要的,好比 生產-消費模式,限流統計等等。什麼 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源碼分析和大數據等多個知識點高級進階乾貨的免費直播講解 能夠進來一塊兒學習交流哦