Java 並發系列之一:java 並發體系


1.  java 並發機制的底層原理實現

 

1.1 volatile

 

1.2 synchronized

1.3 原子操作

2. java 內存模型(JMM)

3. java並發基礎線程

 

4. java 鎖

5. java 並發容器

6. java阻塞隊列(7個)

7. java 並發工具(4個)

8. java 原子操作類Atomic(13個)

9. java並發框架(2個)

 

10. txt

  1 java 並發機制的底層原理實現
  2     volatile
  3         特性
  4             volatile可見性:對一個volatile的讀,總可以看到任意線程對這個變量最終的寫
  5             volatile原子性:volatile對單個讀/寫具有原子性(64位Long、Double),但是復合操作除外,例如 i++
  6             volatile有序性:編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。
  7         內存語義
  8             當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值立即刷新到主內存中
  9             當讀一個volatile變量時,JMM會把該線程對應的本地內存設置為無效,直接從主存中讀取共享變量
 10             volatile的寫-讀與 鎖的釋放-獲取 有相同的內存效果:volatile寫和鎖的釋放有相同的內存語義,volatile讀和鎖的獲取有相同的內存語義。這也是增強volatile的原因,提供了一種比鎖更輕量級的線程之間的通信機制。
 11         重排序規則
 12             1. 當第二個操作是volatile寫操作時,不管第一個操作是什么,都不能重排序
 13             2. 當第一個操作是volatile讀操作時,不管第二個操作是什么,都不能重排序
 14             3. 當第一個操作是volatile寫操作,第二個操作是volatile讀操作,不能重排序
 15         內存語義的實現
 16             內存屏障
 17                 內存屏障是一組處理器指令,用於實現對內存操作的順序限制
 18                 基於保守策略的JMM內存屏障插入策略
 19                     在每個volatile寫操作的前面插入一個StoreStore屏障
 20                         保證之前的都能刷新到主存
 21                     在每個volatile寫操作的后面插入一個StoreLoad屏障
 22                         保證先寫后讀,能提高效率
 23                     在每個volatile讀操作的后面插入一個LoadLoad屏障
 24                     在每個volatile讀操作的后面插入一個LoadStore屏障
 25                 實際執行時,只要不改變volatile寫-讀的內存語義,編譯器可以根據具體情況省略不必要的屏障
 26                 x86處理器只會對寫讀作重排序,故只有一個屏障StoreLoad即可正確實現volatile寫-讀的內存語義
 27         操作系統語義
 28             主存、高速緩存(線程私有)緩存一致?
 29             解決方案
 30                 通過在總線加 Lock 鎖的方式,有些是緩存鎖定
 31                 通過緩存一致性協議 MESI協議(修改、獨占、共享、無效)
 32             實現原則
 33                 Lock前綴指令會引起處理器緩存回寫到內存
 34                 一個處理器的緩存回寫到內存會導致其他處理器的緩存失效
 35         內存模型
 36             內存屏障 限制重排序
 37             happens-before中的volatile規則
 38         volatile VS synchronized
 39             volatile是輕量級的synchronized
 40             使用恰當,它比synchronized使用和執行成本更低,因為不會引起線程上下文的切換和調度
 41     synchronized
 42         同步、重量級鎖
 43         只有使用Synchronized線程處於阻塞,其他Lock, AQS, LockSupport等線程都是處於等待狀態
 44         原理
 45             synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證變量的內存可見性。
 46         鎖對象
 47             1. 普通同步方法鎖,是當前實例對象
 48             2. 靜態同步方法,鎖是當前類的class對象
 49             3. 同步方法塊,鎖是括號里面的對象
 50             java中的每一個對象都可以作為鎖
 51         實現機制
 52             Java對象頭
 53                 synchronized用的鎖是保存在Java對象頭里的
 54                 包括兩部分數據
 55                     Mark Word(標記字段)
 56                         Mark Word被設計成一個非固定的數據結構以便在極小的空間內存存儲盡量多的數據,它會根據對象的狀態復用自己的存儲空間
 57                         包括:哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳
 58                     Klass Pointer(類型指針)  
 59             monitor
 60                 java 中 Object和Class中都關聯了一個Monitor,一個monitor只能被一個線程擁有
 61                 Owner 活動線程
 62                     初始時為NULL表示當前沒有任何線程擁有該monitor record, 當線程成功擁有該鎖后保存線程唯一標識,當鎖被釋放時又設置為NULL
 63                 實現
 64                     同步代碼塊采用 monitorenter、monitorexit指令顯示的同步
 65                     同步方法使用ACC_SYNCHRONIZED標記符隱式的實現
 66         鎖優化
 67             自旋鎖
 68                 該線程等待一段時間,不會被立即掛起,循環看持有鎖的線程是否會很快釋放鎖
 69                 自旋數字難以控制(XX: preBlockSpin)
 70                 存在理論:線程的頻繁掛起、喚醒負擔較重,可以認為每個線程占有鎖的時間很短,線程掛起再喚醒得不償失。
 71                 缺點
 72                     自旋次數無法確定
 73             適應性自旋鎖
 74                 自旋的次數不再是固定的,它是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定
 75                 自旋成功,則可以增加自旋次數,如果獲取鎖經常失敗,那么自旋次數會減少
 76             鎖消除
 77                 若不存在數據競爭的情況,JVM會消除鎖機制
 78                 判斷依據
 79                     變量逃逸
 80             鎖粗化
 81                 將多個連續的加鎖、解鎖操作連接在一起,擴展成一個范圍更大的鎖。例如for循環內部獲得鎖
 82             輕量級鎖
 83                 在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗
 84                 通過CAS(CompareandSwap),即比較並替換,來獲取和釋放鎖
 85                 缺點
 86                     在多線程環境下,其運行效率比重量級鎖還會慢
 87                 性能依據
 88                     對於絕大部分的鎖,在整個生命周期內部都是不會存在競爭的
 89             偏向鎖
 90                 為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執行路徑
 91                 主要盡可能避免不必要的CAS操作,如果競爭鎖失敗,則升級為輕量級鎖
 92     原子操作
 93         處理器
 94             使用總線鎖保證原子性
 95             使用緩存鎖保證原子性
 96         JMM
 97  98                 JVM中除了偏向鎖,其他鎖(輕量級鎖、互斥鎖)的實現方式都用了循環CAS,即當一個線程想進入同步塊的時候使用循環CAS的方式來獲取鎖,當它退出同步塊的時候使用循環CAS釋放鎖。
 99                 JAVA1.6中,鎖一共有4中狀態,級別從低到高依次是:無鎖狀態,偏向鎖狀態,輕量級鎖狀態和重量級鎖狀態
100             CAS
101                 Compare And Swap, 整個JUC體系最核心、最基礎理論,Java中通過鎖和CAS實現原子操作
102                 內存地址V,舊的預期值A,要更新的值B,當且僅當內存地址V中的值等於舊的預期值A時才會將內存值V得值修改為B,否則什么也不干
103                 native中存在四個參數
104                 JVM中的CAS操作利用了處理器提供的CMPXCHG指令實現的。
105                 缺陷
106                     ABA問題
107                         檢查不到值的變化,實際上卻變化了
108                         解決方案
109                             變量前追加版本號版本號
110                             AtomicStampedReference
111                                 這個類的compareAndSet方法的作用是首先檢查當前引用是否等於預期引用,並且檢查當前標志是否等於預期標志,如果全部相等,則以原子的方式將該引用和該標志的值設置為給定的更新值。
112                     循環時間太長
113                         自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷
114                         解決方法
115                             JVM如果能支持處理器提供的pause指令,那么效率一定會提升
116                             pause作用
117                                 1. 可以延遲流水線執行指令(depipeline),使CPU不會消耗過多的執行資源
118                                 2. 避免在退出循環的時候因內存順序沖突而引起的CPU流水線被清空,從而提高CPU的執行效率
119                     只能保證一個共享變量原則操作
120                         對多個共享變量操作時,CAS無法保證操作的原子性,需要用鎖
121                         解決方案
122                             AtomicReference類保證引用對象之間的原子性,可以把多個變量放在一個對象里來進行CAS操作
123                 原子操作類Atomic
124                     java.util.concurrent.atomic里的原子操作類提供了線程安全地更新一個變量的方式
125                     4大類型13個原子操作類
126                         基本類型類
127                             AtomicBoolean
128                             AtomicInteger
129                             AtomicLong
130                         數組
131                             AtomicIntegerArray
132                             AtomicLongArray
133                             AtomicReferenceArray
134                         引用
135                             AtomicReference
136                             AtomicReferenceFieldUpdater
137                             AtomicMarkableReference
138                         屬性
139                             AtomicIntegerFieldUpdater
140                             AtomicLongFieldUpdater
141                             AtomicStampedReference
142                     核心底層
143                         CAS
144                             Unsafe只提供了3中CAS方法
145                                 final native boolean compareAndSwapObject()
146                                 final native boolean compareAndSwapInt()
147                                 final native boolean compareAndSwapLong()
148                 CAS V.S. 鎖
149                     JVM中除了偏向鎖,其他鎖(輕量級鎖、互斥鎖)的實現方式都用了循環CAS,即當一個線程想進入同步塊的時候使用循環CAS的方式來獲取鎖,當它退出同步塊的時候使用循環CAS釋放鎖。
150 java 內存模型(JMM)
151     並發編程的挑戰
152         線程上下文切換
153         死鎖
154         資源限制的挑戰
155     並發編程需要解決的兩大問題
156         線程之間如何通信
157             線程通信機制
158                 內存共享
159                 消息傳遞
160         線程之間如何同步
161             同步是指程序中用於控制不同線間操作發生相對順序的機制
162     內存模型
163         處理器內存模型
164             硬件級的內存模型
165             處理器的內存屏障
166                 內存屏障是一組處理器指令,用於實現對內存操作的順序限制
167                 為了保證內存可見性,Java編譯器再生產指令序列的適當位置會插入內存屏障制定來禁止特定類型的處理器重排序
168                 內存屏障(Memory Barrier,或有時叫做內存柵欄,Memory Fence)是一種CPU指令,用於控制特定條件下的重排序和內存可見性問題。
169                 4大類內存屏障指令
170                     LoadLoad Barriers
171                     LoadStore Barriers
172                     StoreStore Barriers
173                     StoreLoad Barriers
174                         全能型的屏障,同時具有其他三個屏障的效果,但執行該屏障開銷會很大,因為Buffer Fully Flush
175         順序一致性內存模型
176             多線程環境下的理想化的理論參考模型,處理器的內存模型和編程語言的內存模型都會以順序一致性內存模型作為參照
177             為程序提供了極強的內存可見性保證
178             數據競爭
179                 定義
180                     在一個線程中寫一個變量,在另一個線程中讀同一個變量,而且寫和讀沒有通過同步來排序
181                 當程序未正確同步時,就可能會存在數據競爭
182                 如果程序是正確同步的,程序的 執行將具有順序一致性
183                 這里的同步是廣義上的同步,包括常用同步原語的正確使用(Synchronized、volatile 、lock和 final184             特性
185                 一個線程中的所有操作必須按照程序的順序來執行
186                 不管程序是否同步,所有程序都只能看到一個單一的操作執行順序,每個操作都必須原子執行且立即對所有的線程可見
187         JMM
188             語言級的內存模型
189             JMM的內存可見性保證3方面
190                 基本方針:在不改變(正確同步的)程序執行結果的前提下,盡可能地為編譯器和處理器的優化打開方便之門(重排序)
191                 1. 單線程程序。
192                 2. 正確同步的多線程程序。
193                 3. 未同步或未正確同步的多線程程序。
194             happens-before
195                 JMM中最核心的理論,保證跨線程的內存的可見性(可以在一個線程之內,也可以再不用線程之間)通過內存屏障實現
196                 背景
197                     JMM設計的兩大核心目標
198                         為程序員提供足夠強的內存可見性保證
199                         對編譯器和處理器的約束盡可能放松(只要不改變程序的執行結果,編譯器處理器怎么優化都行)首先保證正確性,然后再追求執行效率
200                 簡單定義
201                     在JMM中,如果一個操作執行的結果需要對另一個操作可見,那么這兩個操作之間必須存在happens-before關系
202                 定義
203                     JMM對程序員保證
204                         如果一個操作happens-before另一個操作,那么第一個操作的執行結果對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前
205                     JMM對編譯器和處理器重排序的約束原則
206                         兩個操作之間存在happens-before關系,並不意味着一定要按照happens-before原則判定的順序來執行。如果重排序之后的執行結果與按照happens-before關系來執行的結果一致,那么這種重排序並不非法。
207                 原生java滿足8大規則
208                     1. 程序順序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在后面的操作;
209                     2. 監視器鎖規則:一個unLock操作先行發生於后面對同一個鎖額lock操作;
210                     3. volatile變量規則:對一個變量的寫操作先行發生於后面對這個變量的讀操作;
211                     4. 傳遞規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C;
212                     5. 線程啟動規則:Thread對象的start()方法先行發生於此線程的每個一個動作;
213                     6. 線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生;
214                     7. 線程終結規則:線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行;
215                     8. 對象終結規則:一個對象的初始化完成先行發生於他的finalize()方法的開始;
216                 happens-before與JMM的關系
217                     happens-before規則是JMM呈現給程序員的視圖
218                     一個happens-before規則對應於一個或者多個編譯器和處理器重排序規則
219                 happens-before VS as-if-serial
220                     as-if-serial 語義保證單線程內程序的執行結果不被改變;程序順序執行
221                     happens-before 關系保證正確同步的多線程程序的執行結果不被改變;happens-before制定的順序執行
222 java並發基礎線程
223     線程的狀態
224         6大狀態
225             NEW
226                 初始狀態,線程被構建,但是還沒有調用start()方法
227             RUNNABLE
228                 運行狀態,Java線程將操作系統中的就緒和運行兩種狀態統稱為“運行狀態”
229             BLOCK
230                 阻塞狀態,表示線程阻塞於鎖
231             WAITING
232                 等待狀態,進入該狀態表示當前線程需要等待其他線程做出一些特定動作(通知或者等待)
233             TIME_WAITING
234                 超時等待狀態,可以在等待的時間自行返回的
235             TERMINATED
236                 終止狀態,表示當前線程已經執行完畢
237         Java狀態轉移
238             WAITING-->RUNNABLE
239                 Object.notify()
240                 Object.notifyAll()
241                 LockSupport.unpark(Thread)
242             TIME_WAITING-->RUNNABLE
243                 Object.notify()
244                 Object.notifyAll()
245                 LockSupport.unpark(Thread)
246             BLOCK-->RUNNABLE
247                 獲取到鎖
248             1. 實例化后New
249             2. New-->RUNNABLE
250                 系統調度
251                 Thread.start()
252                     running-->ready
253                         Thread.yield
254                     ready-->running
255             3. RUNNABLE-->WAITING
256                 Object.wait()
257                 Thread.join()
258                 LockSupport.park()
259             4. RUNNABLE-->TIME_WAITING
260                 Object.wait(long)
261                 Thread.sleep(long)
262                 Thread.join(long)
263                 LockSupport.parkNanos()
264                 LockSupport.parkUntil()
265             5. RUNNABLE-->BLOCKED
266                 等待進入synchronized方法
267                 等待進入synchronized塊
268             6. RUNNABLE-->TERMINATED
269             Java 線程狀態變遷
270             yield
271                 暫停當前正在執行的線程對象。
272                 yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。
273                 yield()只能使同優先級或更高優先級的線程有執行的機會。 
274                 注意:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
275         注意
276             等待進入synchronized方法/塊 為阻塞狀態
277             java.concurrent.Lock為   等待狀態,因為Lock接口對於阻塞的實現使用了LockSupport類中的相關方法
278     程序 VS 進程 VS 線程
279         程序
280             一組指令的有序結合,是靜態的指令,是永久存在的
281         進程
282             具有一定獨立功能的程序關於某個數據集合上的一次運行活動,是系統進行資源(打開的文件,創建的socket)分配和調度的一個獨立單元。進程的存在是暫時的,是一個動態的概念。
283         線程
284             線程是CPU調度和分配的基本單位,是比進程更小的能獨立運行的基本單元。本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器、一組寄存器和棧)。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以並發執行。
285         進程 VS 線程
286             定義
287                 進程是資源分配和調度的基本單位;線程是CPU/任務調度和執行的基本單位
288             包含關系
289                 1個進程包含有多(大於等於1)個線程; 線程是進程的一部分(輕量級進程)
290             地址空間
291                 進程之間地址空間獨立; 同一進程內的線程共享本進程的地址空間
292             切換開銷
293                 進程之間切換開銷大; 線程之間切換開銷小(創建和銷毀)
294             創建
295                 進程fork/vfork;    線程pthread_create
296             銷毀
297                 進程結束,它擁有的所有線程都將銷毀; 線程結束,不會影響同個進程中的其他線程
298             私有屬性
299                 進程:PCB(進程控制塊);  線程:TCB(線程控制塊),線程Id,寄存器,上下文
300         一個程序至少只有一個進程,一個進程至少有一個線程
301     啟動和終止線程
302     線程間通信
303         volatile & synchronized
304             共享內存和本地內存拷貝的同步更新問題,使得變量不一定能是最新的
305             volatile: 保證所有線程對變量的可見性
306             synchronized:保證線程對變量訪問的可見性和排他性,獲取monitor; 主要確保多個線程在同一時刻,智能有一個線程處於方法或者同步塊中。
307         等待/通知機制
308             任意java對象所具備的
309             依托於同步機制,目的就是確保等待線程從wait()方法返回時能夠感知到通知線程對變量作出的修改
310             等待通知機制:線程A調用了對象O的wait()方法進入了等待狀態,而線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知后從對象O的wait()方法返回,進而執行后續操作。上述兩個線程通過對象O來完成交互,而對象的wait()與notify()或notifyAll()的關系就如同開關信號一樣,用來完成等待方和通知方之間的交互工作。
311         等待超時模式
312             等待超時模式就是在等待/通知范式基礎上增加了超時控制,避免執行時間過長,也不會“永久”阻塞調用者,而是按照調用者的要求返回。
313             超時等待:調用一個方法時,等待一段時間(一般給定一個時間段),如果該方法能夠在給定的時間段內得到結果,那么將結果立刻返回,反之,超時返回默認結果。
314         管道輸入/輸出流
315             管道輸入輸出流與普通的文件輸入輸出流或者網絡輸入輸出流不同之處在於,它主要用於線程之間的數據傳輸,而傳輸的媒介是內存
316             4種具體的實現
317                 PipedOutputStream
318                 PipedInputStream
319                 PipedReader
320                 PipedWriter
321                 面向字節
322                 面向字符
323         Thread.join()的使用
324             涉及了等待/通知機制,等待前驅線程結束,接收前驅線程結束通知,源碼本質也是wait()和notifyAll()
325         ThreadLocal的使用
326             ThreadLocal,即線程變量,是一個以ThreadLocal對象為鍵,任意對象為值的存儲結構。這個結構被附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
327             ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用。作用:提供一個線程內公共變量,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度,或者為線程提供一個私有的變量副本,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產生影響。
328             方法
329                 initialValue()是一個protected方法,一般是用來在使用時進行重寫的,它是一個延遲加載方法。如果set()方法沒有調用,第一次get()方法調用時會進行初始化 initialValue(),每個線程會調用一次。
330                 get()方法是用來獲取ThreadLocal在當前線程中保存的變量副本
331                 set(T value)用來設置當前線程中變量的副本
332                 remove()用來移除當前線程中變量的副本
333             工作原理
334                 Thread類中有一個成員變量屬於ThreadLocalMap類(一個定義在ThreadLocal類中的內部類),它是一個Map,他的key是ThreadLocal實例對象。
335                 當為ThreadLocal類的對象set值時,首先獲得當前線程的ThreadLocalMap類屬性,然后以ThreadLocal類的對象為key,設定value,值時則類似。
336                 ThreadLocal變量的活動范圍為某線程,是該線程“專有的,獨自霸占”的,對該變量的所有操作均由該線程完成!也就是說,ThreadLocal 不是用來解決共享對象的多線程訪問的競爭問題的,因為ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。當線程終止后,這些值會作為垃圾回收。
337                 由ThreadLocal的工作原理決定了:每個線程獨自擁有一個變量,並非是共享的,
338             存儲結構的好處
339                 1、線程死去的時候,線程共享變量ThreadLocalMap則銷毀。
340                 2、ThreadLocalMap<ThreadLocal,Object>鍵值對數量為ThreadLocal的數量,一般來說ThreadLocal數量很少,相比在ThreadLocal中用Map<Thread, Object>鍵值對存儲線程共享變量(Thread數量一般來說比ThreadLocal數量多),性能提高很多。
341             弱引用GC
342                 1、使用完線程共享變量后,顯示調用remove方法清除線程共享變量可以及時清除
343                 2、JDK建議ThreadLocal定義為private static,這樣ThreadLocal的弱引用問題則不存在了。
344                 3、對於ThreadLocal變量,我們可以手動的將其置為Null,比如tl =null。那么這個ThreadLocal對應的所有線程的局部變量都有可能被回收。
345             解決Hash沖突方法
346                 線性探測
347             應用場景: 用來解決 數據庫連接、Session管理、AOP耗時統計等。
348             注意
349                 不過有點遺憾的是只能放一個值,再次調用set設置值,會覆蓋前一次set的值。如果要多個變量,新建多個ThreadLocal對象
350                 是單個線程內函數和組件的共享變量,不是多線程的共享變量,線程隔離
351 352     Lock接口
353         鎖是用來控制多個線程訪問共享資源的方式。一般來說一個鎖可以防止多個線程同時訪問共享資源(但有些鎖可以允許多個線程訪問共享資源,如讀寫鎖)。
354         在Lock接口出現前,java使用synchronized關鍵字實現鎖的功能,但是在javaSE5之后,並發包中提供了Lock接口(以及其實現類)用來實現鎖的功能。
355         Lock V.S. synchronized 
356             Lock接口有而synchronized不具備的主要特性
357                 嘗試非阻塞地獲取鎖
358                     當前線程嘗試獲取鎖,如果這一時刻鎖沒有被其他線程獲取到,則成功獲取並持有鎖
359                 超時獲取鎖
360                     在指定的截止時間之前獲取鎖,如果截止時間到了仍舊無法獲取鎖,則返回
361                 能被中斷地獲取鎖
362                     與synchronized不同,獲取到所得線程能夠響應中斷,當獲取到鎖的線程被中斷時,中斷異常將會被拋出,同時鎖會被釋放
363             Lock提供了與synchronized相似的功能,但必須顯示的獲取鎖與釋放鎖,雖然不及隱式操作方便,但是擁有了鎖獲取與釋放的可操作性、可中斷的鎖獲取與超時獲取鎖等多重功能。
364     隊列同步器AQS
365         隊列同步器AbstractQueuedSynchronizer(以下簡稱同步器), 是用來構建鎖或者其他同步組件(繼承lock)的基礎框架,
366             它使用了一個int成員變量來表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。
367             同步框架,提供通用機制來原子性管理同步狀態、阻塞和喚醒線程,以及維護被阻塞線程的隊列
368             基於AQS實現的同步器包括:ReentrantLock、Semaphore、ReentrantReadWriteLock、CountDownLatch和FutureTask(任務)
369         使用方式
370             繼承
371                 同步器的主要使用方法是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀態。
372             靜態內部類
373                 子類被推薦定義為自定義同步組件的靜態內部類,同步器自身沒有實現任何同步接口,它僅僅是定義了若干同步狀態獲取和釋放方法來供自定義同步組件使用
374             獨占式共享式獲取同步狀態
375                 同步器即可以支持獨占式獲取同步狀態,也可以支持共享式地獲取同步狀態,這樣方便實現不同類型的同步組件(ReentrantLock、ReentrantReadWriteLock、CountDownLatch等)。
376         鎖 VS 同步器
377             聯系
378                 同步器是實現鎖(也可以是任何同步組件)的關鍵:在鎖中聚合同步器,利用同步器實現鎖的語義。lock方法內部調用實現了AQS的內部類的require方法
379             區別
380                 鎖是面向使用者的,他定義了使用者與鎖交互的接口(比如允許兩個線程並行訪問),隱藏了實現細節;
381                 同步器是面向鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步管理狀態、線程的排隊、等待與喚醒等底層操作。
382                 鎖讓使用者僅僅是調用其方法既可以實現同步效果、同步器讓實現者通過重寫抽象方法進行了隊列的底層操作。他們兩個是使用者和實現者關注不同的領域實現了相同的效果。
383         API
384             AbstractQueuedSynchronizer,同步器,實現JUC核心基礎組件
385             同步器基於模板設計模式實現的,使用者需要繼承同步器並重寫指定的方法,隨后將同步器組合在自定義的同步組件的實現中,並調用同步器提供的模板方法,而這些模板方法會調用使用者重寫的方法。
386             自定義同步組件(extends Lock)將使用同步器 (靜態內部類) 提供的模板方法來實現自己的同步語義
387     重入鎖
388         ReentrantLock
389             可重入鎖,是一種遞歸無阻塞的同步機制
390             比synchronized更強大,靈活的鎖機制,可以減少死鎖發生的概率
391             分為公平鎖和非公平鎖
392             底層采用AQS實現,通過內部Sync繼承AQS
393     讀寫鎖
394         ReentrantReadWriteLock
395             讀寫鎖,兩把鎖:共享鎖-讀鎖、排它鎖-寫鎖
396             支持公平性、非公平性,可重入和鎖降級
397             鎖降級:遵循獲取寫鎖、獲取讀鎖在釋放寫鎖的次序,寫鎖可以降級成為讀鎖。
398     LockSupport工具
399         阻塞和喚醒一個線程
400     Condition接口
401         Lock提供條件Condition,對線程的等待、喚醒操作更加詳細和靈活,依賴於lock
402         Lock提供條件Condition,對線程的等待、喚醒操作更加詳細和靈活
403         內部維護一個Condition隊列,當前線程調用await()方法,將會以當前線程構造一個節點(Node),並將節點加入到該隊列的尾部。
404 java 並發容器
405     ConcurrentHashMap
406         並發編程中需要用到線程安全的HashMap
407         1.8與1.7的區別
408             數據結構
409                 JDK 1.7 數組+鏈表:Segment(ReentrantLock)+HashEntry 
410           JDK 1.8 數組+鏈表/紅黑樹 Node(HashEntry) + Synchronized+CAS+紅黑樹 
411             線程安全
412                 JDK 1.7 Segment(ReentrantLock)
413           JDK 1.8 Synchronized+CAS
414             鎖粒度
415                 JDK1.7  鎖的粒度是基於Segment的,包含多個HashEntry
416           JDK1.8  鎖的粒度是基於Node的(HashEntry首節點),實現降低鎖的粒度
417             查詢時間復雜度
418                 JDK1.7 遍歷鏈表 O(N)
419           JDK1.8 遍歷紅黑樹 O(log N)
420                 鏈表轉化為紅黑樹:定位結點的hash算法簡化會帶來弊端,Hash沖突加劇,因此在鏈表節點數量大於8時,會將鏈表轉化為紅黑樹進行存儲。
421             輔助類
422                 JDK 1.8 例如TreeBin,Traverser等對象內部類。
423     ConcurrentLinkedQueue
424         並發編程中需要用到線程安全的隊列
425             1. 使用阻塞算法
426                 1個鎖(入隊和出隊用同一把鎖)和2個鎖(入隊和出隊用不同的鎖)
427                     阻塞隊列
428             2. 使用非阻塞算法
429                 CAS
430                     ConcurrentLinkedQueue
431         基於鏈接節點的無邊界的線程安全隊列,采用FIFO原則對元素進行排序,內部采用CAS算法實現
432         精妙之處:利用CAS來完成數據操作,同時允許隊列的不一致性,弱一致性表現淋漓盡致。
433     ConcurrentSkipListMap
434         ConcurrentSkipListMap其內部采用SkipLis數據結構實現。
435     ConcurrentSkipListSet
436         內部采用ConcurrentSkipListMap實現
437 Java阻塞隊列(7個)
438     阻塞隊列是一個支持兩個附加操作的隊列
439     兩個附加操作
440         支持阻塞的插入方法
441             當隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿
442         支持阻塞的移除方法
443             當隊列空時,隊列會阻塞獲取元素的線程,直到隊列非空
444     實現原理
445         使用通知模式
446         當生產者往滿的隊列里添加元素時會阻塞住生產者,當消費者消費了一個隊列中的元素后,會通知生產者當前隊列可用
447     一個抽象類
448         AbstractQueue,改類在Queue接口中扮演着非常重要的作用,該類提供了對queue操作的骨干實現
449     一個接口
450         BlockingQueue繼承java.util.Queue為阻塞隊列的核心接口,提供了在多線程環境下的出列、入列操作,作為使用者,則不需要關心隊列在什么時候阻塞線程,什么時候喚醒線程,所有一切均由BlockingQueue來完成。
451     七個阻塞隊列
452         ArrayBlockingQueue
453             一個由數組組成的有界阻塞隊列
454         LinkedBlockingQueue
455             一個由鏈表組成的有界阻塞隊列
456         LinkedBlockingDeque
457             一個由鏈表組成的雙向阻塞隊列
458         PriorityBlockingQueue
459             一個支持優先級排序的無界阻塞隊列
460         DelayQueue
461             一個使用優先級隊列實現的無界阻塞隊列
462         SynchronousQueue
463             一個不存儲元素的阻塞隊列
464         LinkedTransferQueue
465             一個由鏈表組成的無界阻塞隊列
466                 即可以像其他的BlockingQueue一樣有容量又可以像SynchronousQueue一樣不會鎖住整個隊列
467         有界
468         對讀或者寫都是鎖上整個隊列,在並發量大的時候,各種鎖是比較耗資源和耗時間的
469     阻塞隊列的4中處理方式
470         拋出異常
471         返回特殊值
472         一直阻塞
473         超時退出
474         注意: 如果是無界阻塞隊列, 隊列不可能出現滿的情況,使用put或offer方法永遠不會被阻塞,而且使用offer方法時,永遠返回true
475 java 並發工具(4個)
476     CountDownLatch
477         等ABCD 4個人都結束了,自己才能開始,結束一個減一個
478         CountDownLatch 它允許一個或多個線程等待其他N個指定數量線程完成操作
479         CountDownLatch也可以實現join的功能,並且比join的功能更多
480         AQS(隊列同步器) 共享鎖
481         await()
482     CyclicBarrier
483         我和A,B,C,D 5個人互相等待,會合了再一起進電影院,到一個減一個
484         定義
485             可循環使用的同步屏障:它允許一組線程互相等待,直到達到某個公共屏障點(common barrier point)
486             通俗講:讓一組線程到達一個屏障是被阻塞,直到最后一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續干活
487         底層采用ReentrantLock + Condition 實現
488         應用場景
489             多線程結果合並的操作,用於多線程計算數據,最后合並計算結果的應用場景
490     Semaphore
491         計數器,雞蛋籃子里只能放5個雞蛋,缺幾個,才能放幾個
492         信號量
493             計數器,用來控制同時訪問特定資源的線程數量,他通過協調各個線程,以保證合理的使用公共資源
494         內部采用共享鎖實現
495         從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個acquire(),然后再獲取該許可。每個release()添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore只對許可的號碼進行計數,並采取相應的行動。
496         應用場景
497             用於流量控制,特別是公用資源有限的應用場景,比如數據庫連接。
498     Exchanger
499         A和B交換數據
500         定義
501             交換者,是一個用於線程間協作的工具類,用於進行線程間的數據交換,兩個線程
502             它提供一個同步點,用於進行線程間成對配對及交換數據,
503         具體
504             第一個線程先執行exchange()方法,它會一只等待第二個線程也執行exchange()方法,當兩個線程都到達同步點時,兩個線程就可以交換數據,將本線程生產出來的數據傳遞給對方
505         允許在並發任務之間交換數據。具體來說,Exchanger類允許在兩個線程之間定義同步點。當兩個線程都到達同步點時,他們交換數據結構,因此第一個線程的數據結構進入到第二個線程中,第二個線程的數據結構進入到第一個線程中
506         應用場景
507             遺傳算法
508                 交叉
509             校對工作
510                 AB崗錄入電子銀行流水
511             Exchanger 可能被視為 SynchronousQueue 的雙向形式。Exchanger 可能在應用程序(比如遺傳算法和管道設計)中很有用。
512 java 原子操作類Atomic(13個)
513     java.util.concurrent.atomic里的原子操作類提供了線程安全地更新一個變量的方式
514     4大類型13個原子操作類
515         基本類型類
516             AtomicBoolean
517                 原子更新布爾類型
518             AtomicInteger
519                 原子更新整型
520             AtomicLong
521                 原子更新長整型
522             用於通過原子的方式更新基本類型
523         數組
524             AtomicIntegerArray
525                 原子更新整形數組里的元素
526             AtomicLongArray
527                 原子更新長整型數組里的元素
528             AtomicReferenceArray
529                 原子更新引用類型數組里的元素
530             通過原子的方式更新數組里的某個元素
531         引用
532             AtomicReference
533                 原子更新引用類型
534             AtomicReferenceFieldUpdater
535                 原子更新引用類型里的字段
536             AtomicMarkableReference
537                 原子更新帶有標記位的引用類型
538             如果要原子地更新多個變量,就需要使用這個原子更新引用類型提供的類
539         屬性
540             AtomicIntegerFieldUpdater
541                 原子更新整型的字段的更新器
542             AtomicLongFieldUpdater
543                 原子更新長整型的字段的更新器
544             AtomicStampedReference
545                 原子更新帶有版本號的引用類型
546             如果需要某各類的某個字段,使用原子更新字段類
547     核心底層
548         CAS
549             Unsafe只提供了3中CAS方法
550                 final native boolean compareAndSwapObject()
551                 final native boolean compareAndSwapInt()
552                 final native boolean compareAndSwapLong()
553 java並發框架(2個)
554     Fork/Join框架
555         定義
556             一個用於並行執行任務的框架,是一個把大任務分割成若干個小任務,最終匯總每個小任務結果后得到大任務結果的框架
557          核心思想
558              分治
559              fork分解任務,join收集任務
560          工作竊取算法
561             定義
562                  工作竊取算法work-stealing: 某個線程從其他隊列里竊取任務來執行
563             背景
564                 將一個不較大的任務分割為若干個互不依賴的子任務,為了減少線程之間的競爭,把這些子任務分別放到不同的隊列里,並未每個隊列創建一個單獨的線程來執行隊列里的任務,線程和隊列一一對應。
565                  執行快的線程幫助執行慢的線程執行任務,提升整個任務效率
566                 為了減少竊取任務線程和被竊取任務線程之間的競爭,通常會使用雙端隊列
567 竊取任務線程永遠從雙端隊列的尾部拿任務執行
568 被竊取任務線程永遠從雙端隊列的頭部拿任務執行
569             優點
570                 充分利用線程進行並行計算,減少了線程間的競爭
571             缺點
572                 在某些情況下還是存在競爭,比如雙端隊列里只有一個任務時,並且該算法會消耗了更多的資源,比如創建多個線程和多個雙端隊列
573         核心類
574             ForkJoinTask
575                 子類,用於繼承
576                     繼承子類 RecursiveAction
577                         用於沒有返回結果的任務
578                     繼承子類 RecursiveTask
579                         用於有返回結果的任務
580                 方法
581                     fork
582                         分解任務
583                     join
584                         合並任務結果
585                     isCompletedAbnormally()
586                         檢查任務是否已經拋出異常或已經被取消了
587                     getException()
588                         獲取異常
589              ForkJoinWorkerThread
590                  執行任務的工作線程
591             ForkJoinPool
592                  執行任務ForkJoinTask的線程池,ForkJoinTask需要通過ForkJoinPool來執行
593                     submit(task);
594                 內部結構
595                     ForkJoinTask數組
596                         存放任務
597                     ForkJoinWorkerThread數組
598                         執行任務
599     Executor框架
600         Executor框架的結構
601             任務
602                 包括被執行任務需要實現的接口
603                     Runnable接口
604                     Callable接口
605             任務的執行
606                 任務執行機制的核心接口
607                     Executor接口
608                 繼承自Executor的接口
609                     ExecutorService接口
610                         execute(Runnable command)
611                         submit(Runnable task)
612                         submit(Callable<T>task)
613                         返回值是FutureTask對象
614                 實現了ExecutorService接口的實現類
615                     ThreadPoolExecutor
616                     ScheduledThreadPoolExecutor
617             異步計算的結果
618                 Future接口
619                     get
620                         等待任務執行完成
621                     cancel
622                         取消任務完成
623                 實現了Future接口的實現類
624                     FutureTask
625         ThreadPoolExecutor(線程池)
626             是線程池的核心實現類,用來執行被提交的任務
627             ThreadPoolExecutor
628                 corePoolSize
629                     線程池中核心線程的數量。當提交一個任務時,線程池會新建一個線程來執行任務,直到當前線程數等於corePoolSize。如果調用了線程池的prestartAllCoreThreads()方法,線程池會提前創建並啟動所有基本線程。
630                 maximumPoolSize
631                     線程池中允許的最大線程數。線程池的阻塞隊列滿了之后,如果還有任務提交,如果當前的線程數小於maximumPoolSize,則會新建線程來執行任務。注意,如果使用的是無界隊列,該參數也就沒有什么效果了。
632                 keepAliveTime
633                     線程空閑的時間。線程的創建和銷毀是需要代價的。線程執行完任務后不會立即銷毀,而是繼續存活一段時間:keepAliveTime。默認情況下,該參數只有在線程數大於corePoolSize時才會生效。
634                 unit
635                     keepAliveTime的單位。TimeUnit
636                 workQueue
637                     用來保存等待執行的任務的阻塞隊列,等待的任務必須實現Runnable接口。我們可以選擇如下幾種:
638                     分類
639                         ArrayBlockingQueue:基於數組結構的有界阻塞隊列,FIFO。
640                         LinkedBlockingQueue:基於鏈表結構的有界阻塞隊列,FIFO。
641                         SynchronousQueue:不存儲元素的阻塞隊列,每個插入操作都必須等待一個移出操作,反之亦然。
642                         PriorityBlockingQueue:具有優先界別的無界阻塞隊列。
643                 threadFactory
644                     用於設置創建線程的工廠。可以通過線程工廠給每個創建出來的線程設置更有意義的名字,該對象可以通過Executors.defaultThreadFactory()
645                 handler
646                     RejectedExecutionHandler,線程池的拒絕策略。所謂拒絕策略,是指將任務添加到線程池中時,線程池拒絕該任務所采取的相應策略。當向線程池中提交任務時,如果此時線程池中的線程已經飽和了,而且阻塞隊列也已經滿了,則線程池會選擇一種拒絕策略來處理該任務。
647                     四種拒絕策略
648                         AbortPolicy:直接拋出異常,默認策略;
649                         CallerRunsPolicy:用調用者所在的線程來執行任務;
650                         DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;
651                         DiscardPolicy:直接丟棄任務;
652                     當然我們也可以實現自己的拒絕策略,例如記錄日志、持久化存儲不能處理的任務等等,實現RejectedExecutionHandler接口自定義即可。
653             工廠類Executors創建3種類型的ThreadPoolExecutor
654                 SingleThreadExecutor
655                     使用單個worker線程的Executor
656                     應用場景
657                         適用於需要保證順序地執行各個任務;並且在任意時間點,不會有多個線程是活動的應用場景
658                     特點
659                         corePool和maximumPoolSize均被設置為1
660                         使用的是相當於無界的有界阻塞隊列LinkedBlockingQueue,所以帶來的影響和FixedThreadPool一樣。
661                 FixedThreadPool
662                     固定線程數的線程池
663                     應用場景
664                         為了滿足資源管理的需求,需要限制當前線程數量的應用場景
665                         適用於負載比較重的服務器
666                     特點
667                         corePoolSize 和 maximumPoolSize都設置為創建FixedThreadPool時指定的參數nThreads,意味着當線程池滿時且阻塞隊列也已經滿時,如果繼續提交任務,則會直接走拒絕策略
668                         默認的拒絕策略,即AbortPolicy,則直接拋出異常。
669                         keepAliveTime設置為0L,表示空閑的線程會立刻終止。
670                         workQueue則是使用LinkedBlockingQueue,但是沒有設置范圍,那么則是最大值(Integer.MAX_VALUE),這基本就相當於一個無界隊列了。
671                         無界隊列對線程池的影響
672                             1. 當線程池中的線程數量等於corePoolSize 時,如果繼續提交任務,該任務會被添加到無界阻塞隊列workQueue中,因此線程中的線程數不會超過corePoolSize
673                             2. 由於1,使用無界隊列時的 maximumPoolSize是一個無效參數
674                             3. 由於1和2,使用無界隊列時的 keepAliveTime 是一個無效參數
675                             4.  不會拒絕任務
676                 CachedThreadPool
677                     根據需要創建新線程,是大小無界的線程池
678                     應用場景
679                         適用於執行很多的短期異步任務的小程序
680                         適用於負載較輕的服務器
681                     特點
682                         corePool為0,maximumPoolSize為Integer.MAX_VALUE,這就意味着所有的任務一提交就會加入到阻塞隊列中。
683                         keepAliveTime這是為60L,unit設置為TimeUnit.SECONDS,意味着空閑線程等待新任務的最長時間為60秒,空閑線程超過60秒后將會被終止。
684                         阻塞隊列采用的SynchronousQueue,每個插入操作都必須等待另一個線程對應的移除操作,此處把主線程提交的任務傳遞給空閑線程去執行。
685                         SynchronousQueue是一個沒有元素的阻塞隊列,加上corePool = 0 ,maximumPoolSize = Integer.MAX_VALUE,這樣就會存在一個問題,如果主線程提交任務的速度遠遠大於CachedThreadPool的處理速度,則CachedThreadPool會不斷地創建新線程來執行任務,這樣有可能會導致系統耗盡CPU和內存資源,所以在使用該線程池是,一定要注意控制並發的任務數,否則創建大量的線程可能導致嚴重的性能問題。
686                     重要操作
687                         offer
688                             主線程執行offer操作與空閑線程執行的poll操作配對成功后,主線程把任務交給空閑線程執行
689                         execute
690                             執行任務
691                         poll
692                             讓空閑線程在SynchronousQueue中等待60s,如果等待到新任務則執行,否則,空閑線程將終止
693         ScheduledThreadPoolExecutor
694             可以在給定的延遲后運行命令,或者定期執行命令,與Timer類似,但比其功能更強大,更靈活
695         FutureTask
696             實現了Future接口和Runnable接口,代表異步計算的結果,
697             應用場景
698                 當一個線程需要等待另一個線程把某個任務執行完后它才能繼續執行
View Code

 

11. 參考網址:

  1. 參考來源:http://cmsblogs.com/wp-content/resources/img/sike-juc.png
  2. 《Java並發編程的藝術》_方騰飛PDF 提取碼:o9vr
  3. http://ifeve.com/the-art-of-java-concurrency-program-1/
  4. Java並發學習系列-緒論
  5. Java並發編程實戰
  6. 死磕 Java 並發精品合集

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM