java.util.concurrent.locks.LockSupport

要學習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;

問題1parkBlocker又是幹嗎的?工具

原來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個條件

  1. 當配對的unpark發生或者

  2. 配對的unpark已經發生或者線程被中斷時恢復(unpark先行,再執行park)

  3. 當absolute是false時,若是給定的時間是非0(負數)或者給定的時間(正數, 時間單位時毫秒)已通過去了(0的時候會一直阻塞着)。

  4. 當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") ;

         }

     }

相關文章
相關標籤/搜索