Java併發核心淺談

Java併發的核心就是 j.u.c 包,而 j.u.c 的核心是AbstractQueuedSynchronizer抽象隊列同步器,簡稱 AQS,一些鎖啊!信號量啊!循環屏障啊!都是基於AQS。而 AQS 又是基於Unsafe的一系列compareAndSwap,所以理解了這塊,併發不再是問題!

  • 先解釋下何爲compareAndSwap就拿AtomicInteger先開刀:
	// 實際操作的值
	private volatile int value;
	// value 的偏移量 因爲 int 是32位,知道首部地址就可以了
	private static final long valueOffset;
	// 靜態初始化塊,通過虛擬機提供的接口,獲得 valueOffset
	static {
	     try {
	         valueOffset = unsafe.objectFieldOffset
	             (AtomicInteger.class.getDeclaredField("value"));
	     } catch (Exception ex) { throw new Error(ex); }
	 }
	 
	// 只是個封裝方法,起作用的代碼並不在這
	// 值得注意的是顯示的 this 和第三個參數 1
	public final int getAndIncrement() {
	        return unsafe.getAndAddInt(this, valueOffset, 1);
	}
	
	// 以下是 Unsafe 類 可以直接訪問內存地址,類似指針,所以不安全
	// o getAndIncrement()傳入的 this,也就是 AtomicInteger 實例對象
	// offset 內存首部偏移量
	// delta 就是那個 1
	// 應該是希臘字母 δ /'deltə/ delta 變化量,化學反應中的加熱,屈光度,一元二次方程中的判別式
	// 佩服

	public final int getAndAddInt(Object o, long offset, int delta) {
	        int v;
	        do {
	            v = getIntVolatile(o, offset);
	        } while (!compareAndSwapInt(o, offset, v, v + delta));
	        return v;
	}
	
	// 從堆內存獲取最新的 value
	// 如果不明白,可以先了解下 JMM 和 volatile
	public native int getIntVolatile(Object o, long offset);
	
	// expected 就是這個 v = getIntVolatile(o, offset);
	// 意思就是,我給你這個最新的 value,它要是現在 在內存中還是這個值 那你就返回 true,並且把這塊內存上值更新爲 x 
	// 不然的話,我就一直 while (!compareAndSwapInt(o, offset, v, v + delta));
	// 相當於自旋鎖 活鎖,不要被高大上的術語嚇到 就是活的循環,不會像死鎖那樣線程 hang 住
	public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
  • 第二刀就拿ReentrantLock開刀好了,這幾個類中的同步,由於方法和功能不一,細節處理上可能不一樣。但是,原理都是一樣的,離不開上述的CAS
    AQS
	 // The synchronization state. 官方註釋
	 // 我簡單解釋一下,synchronized 原理是給加鎖的對象 加上一個 monitorenter 和 monitorexit 的指令
	 // 當某個線程進入加鎖的代碼(實際上應該是拿到被加鎖的對象在內存的引用地址),會執行 monitorenter 然後將 monitor 置爲1,當別的線程訪問該內存時,發現 monitor 不爲 0
	 // 所以其它線程無法獲得 monitor,直到佔有 monitor 的線程執行 monitorexit 將 monitor 減 1
	 // 如果佔有 monitor 的線程 重複進入,monitor 是可以一直累加的
	 // 瞭解了 synchronized 工作原理,就會明白爲什麼會有諸如 nonfairTryAcquire(1) release(1) 的方法
	 // 這是 AbstractQueuedSynchronizer 類中的字段
	 // 因爲 ReentrantLock 中的內部類 Sync 繼承於 AQS
	private volatile int state;
	
	// tryLock why ?
	// 因爲不同於 synchronized 的悲觀(我纔不管你是不是併發,多線程,聲明瞭,我就加鎖)
	// 所以 ReentrantLock 我先 try 一 try 吧!萬一不是多線程併發呢!🤣
	public boolean tryLock() {
			// 加鎖 加 1
	        return sync.nonfairTryAcquire(1);
	}

	final boolean nonfairTryAcquire(int acquires) {
           final Thread current = Thread.currentThread();
           int c = getState();
           // 0 的話 說明沒有線程佔有
           // 可以獲得鎖
           if (c == 0) {
               // 這個和上面的 AtomicInteger 一樣
               if (compareAndSetState(0, acquires)) {
               	   // 設置當前佔有鎖的線程
                   setExclusiveOwnerThread(current);
                   return true;
               }
           }
           else if (current == getExclusiveOwnerThread()) {
               int nextc = c + acquires;
               // 看見沒,鎖的計數有可能會有問題
               // 因爲一直累計,指不定就加到 int 上限轉負數了
               if (nextc < 0) // overflow
                   throw new Error("Maximum lock count exceeded");
               // 不爲 0 更新 state
               setState(nextc);
               return true;
           }
           return false;
	  }
	  
	// AbstractOwnableSynchronizer 類
 	// The current owner of exclusive mode synchronization.
 	// 排它鎖 獨佔鎖 寫鎖 都一個意思 鎖的當前持有線程
    private transient Thread exclusiveOwnerThread;

總結一下:

Doug Lea 真正的大師,從他的代碼中可以看出對於細節的處理與把控,以及對於我等代碼閱讀者的友好

大道至簡,誰能想到 Java 的併發支持是基於一些加 1 減 1 的運算

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章