要學習JAVA中是如何實現線程間的鎖,就得從LockSupport這個類先提及,由於這個類實現了底層的一些方法,各類的鎖實現都是這個基礎上發展而來的。這個類方法不多,但理解起來須要花費一點時間,由於涉及了不少底層的知識,這些都是咱們平時不關心的。html
上源代碼:java
package java.util.concurrent.locks; import java.util.concurrent.*; import sun.misc.Unsafe; public class LockSupport { private LockSupport() {} // Cannot be instantiated. // Hotspot implementation via intrinsics API private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long parkBlockerOffset; static { try { parkBlockerOffset = unsafe.objectFieldOffset (java.lang.Thread.class.getDeclaredField("parkBlocker")); } catch (Exception ex) { throw new Error(ex); } } private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. unsafe.putObject(t, parkBlockerOffset, arg); } public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); } public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, nanos); setBlocker(t, null); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(true, deadline); setBlocker(t, null); } public static Object getBlocker(Thread t) { return unsafe.getObjectVolatile(t, parkBlockerOffset); } public static void park() { unsafe.park(false, 0L); } public static void parkNanos(long nanos) { if (nanos > 0) unsafe.park(false, nanos); } public static void parkUntil(long deadline) { unsafe.park(true, deadline); } }
這個類提供的都是靜態方法,且沒法被實例化。linux
在LockSupport中有兩個私有的成員變量:編程
private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long parkBlockerOffset;
你們都知道JAVA語言是平臺無關的,一次編譯,能夠在任何平臺上運行,可是若是真的不能夠調用一些平臺相關的方法嗎?其實unsafe類是能夠作到的。api
unsafe:是JDK內部用的工具類。它經過暴露一些Java意義上說「不安全」的功能給Java層代碼,來讓JDK可以更多的使用Java代碼來實現一些本來是平臺相關的、須要使用native語言(例如C或C++)才能夠實現的功能。該類不該該在JDK核心類庫以外使用。 安全
parkBlokcerOffset:parkBlocker的偏移量,從字面上理解是這麼個東東。可是parkBlocker又是幹嗎的?偏移量又是作什麼的呢?讓咱們來看看Thread類的實現:併發
//java.lang.Thread的源碼 /** * The argument supplied to the current call to * java.util.concurrent.locks.LockSupport.park. * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker * Accessed using java.util.concurrent.locks.LockSupport.getBlocker */ volatile Object parkBlocker;
問題1:parkBlocker又是幹嗎的?工具
原來java.lang.Thread的實現當中有這麼一個對象。從註釋上看,這個對象被LockSupport的setBlocker和getBlocker調用。查看JAVADOC會發現這麼一段解釋:佈局
大體意思是,這個對象是用來記錄線程被阻塞時被誰阻塞的。用於線程監控和分析工具來定位緣由的。主要調用了LockSupport的getBlocker方法。
學習
原來,parkBlocker是用於記錄線程是被誰阻塞的。能夠經過LockSupport的getBlocker獲取到阻塞的對象。用於監控和分析線程用的。
問題2:偏移量又是作什麼的?
static { try { parkBlockerOffset = unsafe.objectFieldOffset (java.lang.Thread.class.getDeclaredField("parkBlocker")); } catch (Exception ex) { throw new Error(ex); } }
這個要日後看一下,原來偏移量就算Thread這個類裏面變量parkBlocker在內存中的偏移量。
JVM的實現能夠自由選擇如何實現Java對象的「佈局」,也就是在內存裏Java對象的各個部分放在哪裏,包括對象的實例字段和一些元數據之類。sun.misc.Unsafe裏關於對象字段訪問的方法把對象佈局抽象出來,它提供了objectFieldOffset()方法用於獲取某個字段相對Java對象的「起始地址」的偏移量,也提供了getInt、getLong、getObject之類的方法可使用前面獲取的偏移量來訪問某個Java對象的某個字段。
問題3:爲何要用偏移量來獲取對象?幹嘛不要直接寫個get,set方法。多簡單?
仔細想一想就能明白,這個parkBlocker就是在線程處於阻塞的狀況下才會被賦值。線程都已經阻塞了,若是不經過這種內存的方法,而是直接調用線程內的方法,線程是不會迴應調用的。
private static void setBlocker(Thread t, Object arg)
private static void setBlocker(Thread t, Object arg) { // Even though volatile, hotspot doesn't need a write barrier here. unsafe.putObject(t, parkBlockerOffset, arg); }
參數:
Thread t 須要被賦值Blocker的線程
Object arg 具體的Blocker對象
解讀:有了以前的理解,這個方法就很好理解了。對給定線程t的parkBlocker賦值。爲了防止,這個parkBlocker被誤用,該方法是不對外公開的。
public static Object getBlocker(Thread t)
public static Object getBlocker(Thread t) { return unsafe.getObjectVolatile(t, parkBlockerOffset); }
參數:Thread t, 被操做的線程對象
返回:parkBlocker對象
解讀:從線程t中獲取他的parkerBlocker對象。這個方法是對外公開的。
是否是能夠利用這個方法來寫一個監控程序,炫耀一把.
再講其餘幾個方法以前,先談談park和unpark是作什麼的.
看看SUN的官方解釋 (點擊查看源碼)
/** * Unblock the given thread blocked on <tt>park</tt>, or, if it is * not blocked, cause the subsequent call to <tt>park</tt> not to * block. Note: this operation is "unsafe" solely because the * caller must somehow ensure that the thread has not been * destroyed. Nothing special is usually required to ensure this * when called from Java (in which there will ordinarily be a live * reference to the thread) but this is not nearly-automatically * so when calling from native code. * @param thread the thread to unpark. * */ public native void unpark(Object thread); /** * Block current thread, returning when a balancing * <tt>unpark</tt> occurs, or a balancing <tt>unpark</tt> has * already occurred, or the thread is interrupted, or, if not * absolute and time is not zero, the given time nanoseconds have * elapsed, or if absolute, the given deadline in milliseconds * since Epoch has passed, or spuriously (i.e., returning for no * "reason"). Note: This operation is in the Unsafe class only * because <tt>unpark</tt> is, so it would be strange to place it * elsewhere. */ public native void park(boolean isAbsolute, long time);
字面理解park,就算佔住,停車的時候不就把這個車位給佔住了麼?起這個名字仍是很形象的。unpark,佔住的反義詞,就是釋放。把車從車位上開走。
翻譯一下:
park:阻塞當前線程,(1)當配對的unpark發生或者(2)配對的unpark已經發生或者線程被中斷時恢復(unpark先行,再執行park)。 (3)當absolute是false時,若是給定的時間是非0(負數)或者給定的時間(正數, 時間單位時毫秒)已通過去了(0的時候會一直阻塞着)。(4)當Absolute是true時,若是給定的時間(時間單位是納秒)過去了或者僞造的(在我理解是參數不合法時)線程會恢復中斷。這個操做是不安全的,因此在其餘調用會很奇怪(奇怪?反正就是用的時候要當心)
unpark:當指定線程被park命令阻塞時unpark命令能夠恢復阻塞。在park命令沒有被先調用過的時候,調用unpark,線程仍然不被阻塞。(翻譯的有點那個...).
理解一下,park與unpark命令是成對出現的。unpark必需要在park命令後執行。可是線程的恢復並不必定要用unpark, 由於park的時間參數,有些狀況下線程會本身恢復。
public static void unpark(Thread thread)
public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); }
參數:Thread thread, 須要被停止掛起的線程
帶blocker參數的park方法
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, nanos); setBlocker(t, null); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(true, deadline); setBlocker(t, null); }
參數:
Object blocker:用於記錄到線程中的parkBlocker對象。
nanos:在nanos時間後線程自動恢復掛起
deadline:在deadline時刻線程自動(這個毫秒其實就是自1970年1月1日0時起的毫秒數)
解讀:這三個方法實際上是一個意思,把blocker放到線程當中,注意,這個park方法是一個阻塞的方法,除非4個條件
當配對的unpark發生或者
配對的unpark已經發生或者線程被中斷時恢復(unpark先行,再執行park)
當absolute是false時,若是給定的時間是非0(負數)或者給定的時間(正數, 時間單位時毫秒)已通過去了(0的時候會一直阻塞着)。
當Absolute是true時,若是給定的時間(時間單位是納秒)過去了或者僞造的(在我理解是參數不合法時)線程會恢復中斷。
不帶blocker參數的park方法
public static void park() { unsafe.park(false, 0L); } public static void parkNanos(long nanos) { if (nanos > 0) unsafe.park(false, nanos); } public static void parkUntil(long deadline) { unsafe.park(true, deadline); }
這三個方法跟上面同樣,惟一區別是沒有作parkBlocker的賦值操做。
來自我同事的併發編程網:
咱們繼續看一下JVM是如何實現park方法的,park在不一樣的操做系統使用不一樣的方式實現,在linux下是使用的是系統方法pthread_cond_wait實現。實現代碼在JVM源碼路徑src/os/linux/vm/os_linux.cpp裏的 os::PlatformEvent::park方法,代碼以下:
void os::PlatformEvent::park() {
int v ;
for (;;) {
v = _Event ;
if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;
}
guarantee (v >= 0, "invariant") ;
if (v == 0) {
// Do this the hard way by blocking ...
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
guarantee (_nParked == 0, "invariant") ;
++ _nParked ;
while (_Event < 0) {
status = pthread_cond_wait(_cond, _mutex);
// for some reason, under 2.7 lwp_cond_wait() may return ETIME ...
// Treat this the same as if the wait was interrupted
if (status == ETIME) { status = EINTR; }
assert_status(status == 0 || status == EINTR, status, "cond_wait");
}
-- _nParked ;
// In theory we could move the ST of 0 into _Event past the unlock(),
// but then we'd need a MEMBAR after the ST.
_Event = 0 ;
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
}
guarantee (_Event >= 0, "invariant") ;
}
}