耐心看完的你或多或少會有收穫!html
Java併發的核心就是 java.util.concurrent 包,而 j.u.c 的核心是AbstractQueuedSynchronizer
抽象隊列同步器,簡稱 AQS,一些鎖啊!信號量啊!循環屏障啊!都是基於AQS。而 AQS 又是基於Unsafe
的一系列compareAndSwap
,因此理解了這塊,併發再也不是問題!java
先解釋下何爲compareAndSwap
,就拿AtomicInteger
來舉例了:c++
// 實際操做的值
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 的相關方法,已棄用併發
/** 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 源碼(頁面左側提供下載和瀏覽的連接)ui
// 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 真正的大師,從他的代碼中能夠看出對於細節的處理與把控,以及對於我等代碼閱讀者的友好this
大道至簡,誰能想到 Java 的併發支持是基於一些加 1 減 1 的運算spa
synchronized
的 Monitor 和各類鎖的 tryAcquire(1) tryRelease(1)
是否是很像🤣.net
若是有幫助你理解併發,那麼你也能夠再結合源碼好好感覺下,請注意tryAcquire(1) tryRelease(1)
這兩種方法(方法名或參數可能略有不一樣)