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和 final) 184 特性 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 當一個線程需要等待另一個線程把某個任務執行完后它才能繼續執行
11. 參考網址:
- 參考來源:http://cmsblogs.com/wp-content/resources/img/sike-juc.png
- 《Java並發編程的藝術》_方騰飛PDF 提取碼:o9vr
- http://ifeve.com/the-art-of-java-concurrency-program-1/
- Java並發學習系列-緒論
- Java並發編程實戰
- 死磕 Java 並發精品合集