背景
編寫一個單例的實現(xiàn)。這里采用一個雙重檢查方式。
image.png
發(fā)現(xiàn)是有問題的。主要是編譯優(yōu)化導致new 對象的順序和可見性問題。
問題修復 :只需要再單例對象 加上 volatile 修飾即可。
分析背后的原因
多線程編程中主要核心關注如下三個點 :
可見性
緩存帶來的原子性 (硬件的坑)
原子性
線程切換帶來的原子性
有序問題
編譯優(yōu)化,內存模型一般原則(HAppens-Before)
image.png
這里給出一個volatile 修飾的對象 new 對象在編譯后的執(zhí)行字節(jié)碼流程,可以看出沒有做編譯優(yōu)化和順序重排。
JAVA并發(fā)常見的內存模型
valatile :禁
這條規(guī)則是指一個valatile 變量的寫操作
Happens-Before 于后續(xù)對這個 valatile變量的讀操作
le變量的讀操作
final :
編譯優(yōu)化更好點 ,生而不變,可以使勁的優(yōu)化
傳遞性:
Happens-Before: 前面的操作結果對后續(xù)的操作是可見的
x = 7 y = 8 在多線程中,
傳遞性 是指 :
線程A:寫 X =7 Y =8 成功
線程B:如果讀到 y = 8 ,
那么他讀到的X 肯定是7
Java 采用的是管程技術,synchronized 關鍵字及 wait()、notify()、notifyAll() 這三個方法都是管程的組成部分。
而管程和信號量是等價的,所謂等價指的是用管程能夠實現(xiàn)信號量,也能用信號量實現(xiàn)管程。但是管程在利用OOP的封裝特性解決了信號量在工程實踐上的復雜性問題,因此java采用管理機制。
因此java
就是將共享變量及其對共享變量的操作統(tǒng)一封裝起來。在下圖中,管程 X 將共享變量 queue 這個隊列和相關的操作入隊 enq()、出隊 deq() 都封裝起來了;線程 A 和線程 B 如果想訪問共享變量 queue,只能通過調用管程提供的 enq()、deq() 方法來實現(xiàn);enq()、deq() 保證互斥性,只允許一個線程進入管程。管程模型和面向對象高度契合的。
image.png
管程如何解決線程間的同步問題呢?
Java 參考了 MESA 模型,語言內置的管程(synchronized)對 MESA 模型進行了精簡。MESA 模型中,條件變量可以有多個,Java 語言內置的管程里只有一個條件變量。具體如下圖所示。
image.png
JAVA SDK并發(fā)包通過Lock 和Condition兩個接口’又’實現(xiàn)了一次管程。LOCK 解決互斥,Condition解決同步問題
重復造輪子
1.能夠響應終端
2.支持超時
3. 非阻塞獲取鎖
image.png
輪子的理由:
1.能夠響應終端
2.支持超時
先找到一個 阻塞的線程 看執(zhí)行棧信息
image.png
image.png






