1. 程式人生 > 其它 >JVM學習-CAS與原子類

JVM學習-CAS與原子類

技術標籤:JVM多執行緒Java多執行緒javajvm

1.CAS

CAS 即 Compare and Swap ,它體現的一種樂觀鎖的思想,比如多個執行緒要對一個共享的整型變數執 行 +1 操作:

// 需要不斷嘗試 
while(true) {   
	int 舊值 = 共享變數 ; // 比如拿到了當前值 0  
	int 結果 = 舊值 + 1; // 在舊值 0 的基礎上增加 1 ,正確結果是 1  
	   
	/*  這時候如果別的執行緒把共享變數改成了 5,本執行緒的正確結果 1 就作廢了,這時
		compareAndSwap 返回 false,重新嘗試,直到:    
		compareAndSwap 返回 true,表示我本執行緒做修改的同時,別的執行緒沒有干擾  
	*/
//這裡其實是把共享變數的值與舊值做比較,如果相同,就可以安全的把結果寫入共享變數 if( compareAndSwap ( 舊值, 結果 )) { // 成功,退出迴圈 } }

CAS對共享變數可以不用加鎖。
**獲取共享變數時,為了保證該變數的可見性,需要使用 volatile 修飾。**結合 CAS 和 volatile 可以實現無 鎖併發,適用於競爭不激烈、多核 CPU 的場景下

  • 因為沒有使用 synchronized,所以執行緒不會陷入阻塞,這是效率提升的因素之一
  • 但如果競爭激烈,可以想到重試必然頻繁發生,反而效率會受影響
    執行緒陷入阻塞,會涉及到執行緒的上下文切換,這樣就嚴重降低了效率。

CAS 底層依賴於一個 Unsafe 類來直接呼叫作業系統底層的 CAS 指令,下面是直接使用 Unsafe 物件進 行執行緒安全保護的一個例子

import sun.misc.Unsafe; 
import java.lang.reflect.Field; 
public class TestCAS {    
	public static void main(String[] args) throws InterruptedException {
		DataContainer dc = new DataContainer();        
		int count = 5;
		Thread t1 =
new Thread(() -> { for (int i = 0; i < count; i++) { dc.increase(); } }); t1.start(); t1.join(); System.out.println(dc.getData()); } } class DataContainer{ private volatile int data; static final Unsafe unsafe; static final long DATA_OFFSET; static { try { // Unsafe 物件不能直接呼叫,只能通過反射獲得 Field theUnsafe =Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); unsafe = (Unsafe) theUnsafe.get(null); }catch(NoSuchFieldException | IllegalAccessException e) { throw new Error(e); } try { // data 屬性在 DataContainer 物件中的偏移量,用於 Unsafe 直接訪問該屬性。 //方便定位data的地址。 DATA_OFFSET= unsafe.objectFieldOffset(DataContainer.class.getDeclaredField("data")); } catch (NoSuchFieldException e) { throw new Error(e); } } public void increase(){ int oldValue; while(true) { // 獲取共享變數舊值,可以在這一行加入斷點,修改 data 除錯來加深理解 oldValue = data; // cas 嘗試修改 data 為 舊值 + 1,如果期間舊值被別的執行緒改了,返回 false /*第一個引數當前DataContainer物件,第二個引數偏移量。 對前面兩個資訊我們知道,針對哪個欄位做compareandSwap。 後面的引數就是舊值和新值,它首先會把oldValue與共享變數的當前值做比較。 如果兩個不一致,表明修改失敗,然後我就重新進入while迴圈,直到舊值和共享變數的值一致了。 然後compareandswap才會返回true,並且新值更新到共享變數data裡去。 if條件成立,while就結束了 */ if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue + 1)) { return; } } } public void decrease() { int oldValue; while(true) { oldValue = data; if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue - 1)){ return; } } }

2 樂觀鎖與悲觀鎖

CAS 是基於樂觀鎖的思想:樂觀的估計,不怕別的執行緒來修改共享變數,就算改了也沒關係, 我吃虧點再重試唄。
synchronized 是基於悲觀鎖的思想:悲觀的估計,得防著其它執行緒來修改共享變數,我上了鎖 你們都別想改,我改完了解開鎖,你們才有機會

3 原子操作類

juc(java.util.concurrent)中提供了原子操作類,可以提供執行緒安全的操作,例如:AtomicInteger、 AtomicBoolean等,它們底層就是採用 CAS 技術 + volatile 來實現的。 可以使用 AtomicInteger 改寫之前的例子:

// 建立原子整數物件 
private static AtomicInteger i = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
	Thread t1 = new Thread(() -> {
		for (int j = 0; j < 5000; j++) {
			i.getAndIncrement();  // 獲取並且自增  i++ 
			//                i.incrementAndGet();  // 自增並且獲取  ++i        
		}    
	});
	Thread t2 = new Thread(() -> {
		for (int j = 0; j < 5000; j++) {
			i.getAndDecrement();  // 獲取並且自增  i++ 			
		} 
	});
	 t1.start();
	 t2.start();
	 t1.join();
	 t2.join();
	 System.out.println(i);
}

在這裡插入圖片描述