1.Java記憶體模型:JMM
在記憶體模型當中定義一個主記憶體,所有聲明的執行個體變量都存在于主記憶體當中,主記憶體的資料會共享給所有線程,每一個線程有一個塊工作記憶體,工作記憶體當中主記憶體資料的副本當更新資料時,會将工作記憶體中的資料同步到主記憶體當中;
2.什麼是CAS
CAS:Compare and Swap,即比較交換;
jdk1.5增加了并發包java.util.concurrent.*,其下面的類使用CAS算法實作了差別于synchronized同步鎖的一種樂觀鎖。jdk1.5之前java語言是靠synchronized關鍵字保證同步的,這是一種獨占鎖,也是悲觀鎖;
3.CAS算法了解
3.1 與鎖相比,使用比較交換會使程式看起來更加複雜一些。但由于其非阻塞性,它對死鎖問題天生免疫,并且,線程間的互相影響也遠遠比基于鎖的方式要小;更為重要的是,使用無鎖的方式完全沒有鎖競争帶來的系統開銷,也沒有線程間頻繁排程帶來的開銷;是以,它要比基于鎖的方式擁有更優越的性能;
3.2 無鎖的好處:
3.2.1 在高并發情況下,它比有鎖的程式擁有更好的性能;
3.2.2 它天生就是死鎖免疫的;
3.3 CAS算法的過程:
它包含三個參數CAS(V,E,N):V表示更新的變量,E表示預期值,N表示新值;
3.3.1 線程通路時,先會将主記憶體中的資料同步到線程的工作記憶體當中
3.3.2 假設線程A和線程B都有對資料進行更改,那麼假如線程A先擷取到執行權限
3.3.3 線程A先會對比工作記憶體當中的資料和主記憶體當中的資料是否一緻,如果一緻(V==E)則進行更新,不一緻則重新整理資料,重新循環判斷
3.3.4 這時更新完畢後,線程B也要進行資料更新,主記憶體資料和工作記憶體資料做對比,如果一緻則進行更新,不一緻則将主記憶體資料重新更新到工作記憶體,然後循環再次對比兩個記憶體中的資料直到一緻為止
3.4 CAS操作是抱着樂觀的态度進行的,它總是認為自己可以成功完成操作;當多個線程同時使用CAS操作一個變量時,隻有一個會勝出,并成功更新,其餘均會失敗;失敗的線程不會被挂起,僅是被告知失敗,并且允許再次嘗試,當然也允許失敗的線程放棄操作;基于這樣的原理,CAS操作即使沒有鎖,也可以發現其他線程對目前線程的幹擾,并進行恰當的處理;
3.5 簡單的說,CAS需要你額外給出一個期望值,也就是你認為這個變量現在應該是什麼樣子的;如果變量不是你想象的那樣,那說明它已經被别人修改過了;你就要重新讀取,再次嘗試修改就好了;
3.6 在硬體層面,大部分的現代處理器都已經支援原子性的CAS指令;在jdk1.5以後,虛拟機便可以使用這個指令來實作并發操作和并發資料結構,并且,這種操作在虛拟機中可以說是無處不在;
4.CAS缺點
CAS存在一個很明顯的問題,即ABA問題;
問題:如果變量V初次讀取的時候是A,并且在準備指派的時候檢查到它任然是A,那能說明它的值沒有被其他線程修改過了嗎?
如果在這段時間曾經被改成B,然後有改回A,那CAS操作就會誤任務它從來沒有被修改過。正對這種情況,java并發包提供了一個帶有标記的原子應用類AtomicStampedRefernce,它可以通過變量值的版本來保證CAS的正确性;
5.原子類
java中的原子類大緻可以分為四個類:
原子更新基本類型;
原子更新數組類型;
原子更新引用類型;
原子更新屬性類型;
這些原子類中都是用了無鎖的概念,有的地方直接使用CAS操作的線程安全的類型;
public class AtomicIntegerTest implementsRunnable {private static Integer count=1;private static AtomicInteger atomicInteger=newAtomicInteger();
@Overridepublic voidrun() {while (true){int count=getCountAtomic();
System.out.println(count);if (count>=150){break;
}
}
}public synchronizedInteger getCount(){try{
Thread.sleep(50);
}catch(InterruptedException e) {
e.printStackTrace();
}return count++;
}publicInteger getCountAtomic(){try{
Thread.sleep(50);
}catch(InterruptedException e) {
e.printStackTrace();
}returnatomicInteger.incrementAndGet();
}public static voidmain(String[] args){
AtomicIntegerTest test= newAtomicIntegerTest();
Thread thread1= newThread(test);
Thread thread2= newThread(test);
thread1.start();
thread2.start();
}
}