耐心看完的你或多或少會有收穫!html
Java併發的核心就是 java.util.concurrent 包,而 j.u.c 的核心是AbstractQueuedSynchronizer
抽象隊列同步器,簡稱 AQS,一些鎖啊!信號量啊!循環屏障啊!都是基於AQS。而 AQS 又是基於Unsafe
的一系列compareAndSwap
,因此理解了這塊,併發再也不是問題!java
但願你已經瞭解了 Java內存模型c++
先解釋下何爲compareAndSwap
,就拿AtomicInteger
來舉例了:安全
// 實際操做的值 private volatile int value; // value 的偏移量 由於 int 是32位,知道首部地址就能夠了 private static final long valueOffset; // 靜態初始化塊,經過虛擬機提供的接口,得到 valueOffset = 12 // 不論你實例化多少個 AtomicInteger 對象,這些對象儘管指向不一樣的堆內存,可是結構都是同樣的 // 因此初始化一次就行了 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);
知道了CAS
就能夠進一步的說說ReentrantLock
, 若是未曾瞭解Java
對象結構,建議先了解下 Java 對象的內存結構多線程
AQS
的幾個子類,因爲方法和功能不一,在同步處理的細節上可能不同。可是,原理都是同樣的,離不開上述的CAS
併發
// 我先簡單解釋一下 synchronized 工做原理 首先它含有 monitorenter 和 monitorexit 兩條指令 // Java 中的每一個對象都有本身的 Monitor(由HotSpot c++ 實現) // 當某個線程進入加鎖的代碼(實際上應該是拿到被加鎖的對象在內存的引用地址) // 會執行 monitorenter 而後將 monitor 置爲1,當其它線程訪問該內存時,發現 monitor 不爲 0 // 因此其它線程沒法得到 monitor,直到佔有 monitor 的線程執行 monitorexit 退出將 monitor 減 1 // 若是佔有 monitor 的線程重複進入,monitor 是能夠一直累加的(可重入鎖,例如經過遞歸或方法互調) // 瞭解了 synchronized 基本的工做原理,就會明白爲何會有諸如 nonfairTryAcquire(1) release(1) 的方法 // 這是 AbstractQueuedSynchronizer 類中的字段 // 由於 ReentrantLock 中的內部類 Sync 繼承於 AQS // The synchronization state private volatile int state; // tryLock why ? // 由於不一樣於 synchronized 的悲觀(我才無論你是否是併發,多線程,聲明瞭,我就加鎖) // 因此 ReentrantLock 我先 try 一 try 吧!萬一不是多線程併發呢!🤣 public boolean tryLock() { // 加鎖 加 1 return sync.nonfairTryAcquire(1); } public void unlock() { // 釋放鎖 減 1 sync.release(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;
Unsafe monitor 的相關方法,已棄用ui
/** Lock the object. It must get unlocked via {@link #monitorExit}. */ @Deprecated public native void monitorEnter(Object o); /** * Unlock the object. It must have been locked via {@link * #monitorEnter}. */ @Deprecated public native void monitorExit(Object o); /** * Tries to lock the object. Returns true or false to indicate * whether the lock succeeded. If it did, the object must be * unlocked via {@link #monitorExit}. */ @Deprecated public native boolean tryMonitorEnter(Object o);
如下爲 hotspot c++ 源碼的一部分代碼,想追根溯源的能夠了解下 hotspot 源碼(頁面左側提供下載和瀏覽的連接)this
// Enter support void ObjectMonitor::enter(TRAPS) { // The following code is ordered to check the most common cases first // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. Thread * const Self = THREAD; void * cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL); if (cur == NULL) { // Either ASSERT _recursions == 0 or explicitly set _recursions = 0. assert(_recursions == 0, "invariant"); assert(_owner == Self, "invariant"); return; } if (cur == Self) { // TODO-FIXME: check for integer overflow! BUGID 6557169. // 看來出現過 count overflow bug _recursions++; // recursion 遞歸說明了可重入 return; } // 省略 } void ObjectMonitor::exit(bool not_suspended, TRAPS) { Thread * const Self = THREAD; if (THREAD != _owner) { if (THREAD->is_lock_owned((address) _owner)) { // Transmute _owner from a BasicLock pointer to a Thread address. // We don't need to hold _mutex for this transition. // Non-null to Non-null is safe as long as all readers can // tolerate either flavor. assert(_recursions == 0, "invariant"); _owner = THREAD; _recursions = 0; } else { // Apparent unbalanced locking ... // Naively we'd like to throw IllegalMonitorStateException. // As a practical matter we can neither allocate nor throw an // exception as ::exit() can be called from leaf routines. // see x86_32.ad Fast_Unlock() and the I1 and I2 properties. // Upon deeper reflection, however, in a properly run JVM the only // way we should encounter this situation is in the presence of // unbalanced JNI locking. TODO: CheckJNICalls. // See also: CR4414101 TEVENT(Exit - Throw IMSX); assert(false, "Non-balanced monitor enter/exit! Likely JNI locking"); return; } } if (_recursions != 0) { _recursions--; // this is simple recursive enter TEVENT(Inflated exit - recursive); return; } // 省略 }
Doug Lea 真正的大師,從他的代碼中能夠看出對於細節的處理與把控,以及對於我等代碼閱讀者的友好spa
大道至簡,誰能想到 Java 的併發支持是基於一些加 1 減 1 的運算.net
synchronized
的 Monitor 和各類鎖的 tryAcquire(1) tryRelease(1)
是否是很像🤣
若是有幫助你理解併發,那麼你也能夠再結合源碼好好感覺下,請注意tryAcquire(1) tryRelease(1)
這兩種方法(方法名或參數可能略有不一樣)