德陽seo網站建設重慶seo
1.java內存模型
1.1 原子性
?
1.2 問題分析
這里與局部變量自增不同,局部變量調用iinc是在局部變量表槽位上進行自增。?
靜態(tài)變量是在操作數棧自增。
這里的主內存和工作內存時再JMM里的說法。
?因為操作系統(tǒng)是時間片切換的多個線程輪流使用CPU.
1.3解決方法
JMM中通過synchronized(同步關鍵字)保證原子性。
使用synchronized減i++和i--的分別的所有字節(jié)碼指令作為一個整體運行。
使用synchronized加鎖的力度最好大一點,只鎖個i++就只有四條指令,不然增加時間。
2.可見性
2.1退不出的循環(huán)
運行發(fā)現停不下來了。
運行超過一秒之后就觸發(fā)C2編譯器進行優(yōu)化了。run被讀到了線程的局部變量表里面。
1s后再修改也看不見了。
?
2.2解決方法
2.3可見性
volatile只適用于一個寫線程和多個讀線程的情況。
println底層有synchronized關鍵字,也可以強制線程去到主存里面取值。
synchronized可以保證可見性和原子性。
3.有序性
3.1詭異的結果
應該是指令順序為了優(yōu)化發(fā)生了改變導致ready=true時num還沒獲取到2。?
3.2解決方法
@Outcome注解就是檢查感興趣的結果。
1或4就劃分為可接受的,ok表示之中
0劃分到另一個。
清除并重新編譯
生成一個源碼jar包和一個壓測入口jar包。?
?
?運行測試包進行壓測。
結果中有兩種,一個是帶了關閉了分層編譯,還有一個是沒帶任何參數。
?兩種情況都有出現0的結果。
說明指令重排問題確實有。
解決方法就是使用volatie關鍵字。?
再次壓測就不會有指令重排的問題了。
3.3有序性理解
雙重檢測法創(chuàng)建單例 就需要volatile防止指令重排
?
如果在創(chuàng)建的代碼上加鎖力度就太大了,創(chuàng)建該對象了,后續(xù)get該對象是不需要加鎖的。
所以有了上面的雙重判斷,先判斷是否實例為空,為空就加鎖,加完鎖再判斷實例為空,任然為空就創(chuàng)建。
第一個if是為了提高效率,實例創(chuàng)建后,就不用一直獲得鎖對象。
第二個if是防止別的線程創(chuàng)建另一個實例。
?0分配空間,3復制多一個引用進操作數棧4一個引用去調用構造方法7另一個引用交給了靜態(tài)變量
t2直接返回了拿到了一個不完整的實例。
3.4 happens-before
就相當于打個標記,標記前改了值, 標記后看得見?
?
4.CAS與原子類
4.1 CAS
CAS 是?Compare And Swap(比較并替換)的縮寫,當值為預期值的時候,就將該值替換為預期的值。
CAS 也是實現原子操作的一種方法。
?
??
4.2樂觀鎖與悲觀鎖
4.3原子操作類
測試結果為0。?
5.synchronized 優(yōu)化(這個部分要先去看JUC)
5.1輕量級鎖
A有兩次加鎖,一次輕量鎖,一次重量鎖。升級過程就是鎖膨脹。
輕量級鎖的加鎖過程。
線程和對象之間交換定情信物,對象給出了Mark World存在線程的鎖記錄里面,線程給出了鎖記錄地址。
Mark Word只有八個字節(jié),解鎖時才會將對象的Mark Word恢復。?
鎖了A之后去訪問B嘗試鎖B結果發(fā)現已經鎖了,但是是自己上的鎖所以還是可以訪問B.
然后都訪問完了之后就A和B都解鎖。
解鎖過程是把MarkWorld都還回去然后取出對象上的鎖標記。?
5.2鎖膨脹
?
?升級為重量級鎖會把標記從01變成10,并在對象頭里面加入重量級鎖的指針,該指針用于線程1在解鎖時喚醒阻塞中的線程。?
5.3重量鎖_自旋
這里線程2不會立刻?阻塞,阻塞需要把當前狀態(tài)保存下來。
直接采用了自旋優(yōu)化,先不停,不停重試,在閾值之內等到了對象解鎖
?自旋失敗 就進入阻塞狀態(tài)了。
5.4 偏向鎖
鎖重入就是要鎖不同的代碼塊時對同一個對象加鎖。
?
?5.5其它優(yōu)化
上鎖時間過長可能會導致輕量鎖變成重量鎖。