Hello,你們好,今天開始Java併發編程序列的第二篇,該篇主要講解如何使用synchronized實現同步,以及volatile關鍵字的做用,最後講解線程間如何進行通信。文章結構:java
synchronized關鍵字的用法我就很少說了。網上爛大街,N年前的技術了。我先說下結論,而後說下底層實現原理:算法
實現原理: 編程
synchronized同步代碼塊原理很簡單,兩個字節碼指令,一個monitorenter,一個monitorexit,不管多少個線程,一次只能一個進入到monitorenter,其餘的進入BLOCKED狀態阻塞。而後來張效果圖: 安全
volatile關鍵字也比較簡單,仍是老樣子,說下結論,講下原理:多線程
先看下JMM內存模型。 併發
再來看下volatile達到的效果:int a = 0;
bool flag = false;
public void write() {
a = 2; //1
flag = true; //2
}
public void multiply() {
if (flag) { //3
int ret = a * a;//4
}
}
複製代碼
這個代碼,1和2步驟不必定是先執行1,後執行2.有可能先執行2,再執行1,多線程環境下,會致使ret的值爲0(不是預期的4),達不到預期效果。因此咱們要避免重排序。把a變量設置爲volatile變量,這樣就是順序執行了,先執行1,再執行2.spa
這個更easy了。好比有一個共享volatile變量value=0;兩個線程同時讀取出去,而後都執行++操做爲1,賦值的時候A線程賦值爲1了,B線程又去賦值,把以前的1給覆蓋爲1了。因此兩個線程++後的結果不是預期的2,而是1.線程
說下volatile的底層原理: 其實就是在volatile變量的前面加上了一個lock彙編指令,有以下效果:3d
說下volatile的適用場景:code
上面提到了volatile不保證原子性,so,怎麼辦?其一,比較粗暴的加鎖.其次就是比較出名的Cas算法了。這裏順帶說一下Cas算法。Cas算法的意思就是Compare and set .意思就是,在設置一個變量的值的時候,先拿舊值和它比一下,若是同樣,再set.這樣就避免了多線程間的髒寫。好比一個變量值爲1 ,被A線程更改爲了2,B線程在更改時判斷它以前拿到的1和如今的2不同,就不進行寫操做了。JVM中的Cas操做是利用了一個處理器指令CMPXCHG。 自旋Cas:不斷的獲取,進行Cas操做。只到成功: JUC中提供了相似於AtomicInteger的原子類,提供了compareAndSet方法,來支持Cas算法。須要注意的是AutomicReference能夠變向的支持多變量原子操做. 下面我來寫一個可以保證多線程安全的原子++操做的方法:
AutomicInteger safeI =0;
private void safeAddOne(){
for(;;){
int i =safeI.get();
boolean suc = safeI.compareAndSet(i,++i);
if(suc){
break;
}
}
}
複製代碼
這個方法,不管被多個個線程併發調用,最終的結果都是依次+1後的結果,不會存在覆蓋。
線程間通信wait,notify也是必須須要掌握的。這一篇只講wait和notify通信。其實JUC以後有更好的通信方式。後期講JUC時專門講。仍是老樣子,先列知識點,再講原理:
注意圖中兩條觸發線:
而後丟一個典型的 等待/通知的模型:
等待方:
synchronized(對象) {
while(條件不知足) {
對象.wait();
}
對應的處理邏輯
}
通知方:
synchronized(對象) {
改變條件
對象.notifyAll();
}
複製代碼
注意點:
好了,其實JUC以前的線程的知識並不難,因此我寫的也不是很細。不太重點都出來了。Have a good day .後期講JUC的時候可就沒這麼Easy了。