【java基礎】線程筆記——LockSupport

LockSupport

這裏寫圖片描述

LocalSupport類特性

  • 不可實例化
private LockSupport() {} // Cannot be instantiated.
  • LockSupport的方法都是靜態方法

  • 私有變量

    private static final sun.misc.Unsafe UNSAFE;
    private static final long parkBlockerOffset;

私有變量:unsafe

  1. sun.misc.Unsafe可以直接操控內存。被JDK廣泛用於自己的包中,如java.nio和java.util.concurrent
  2. API十分不安全、不輕便、而且不穩定。不建議在生產環境中使用
  3. 這個不安全的類提供了一個觀察HotSpot JVM內部結構並且可以對其進行修改。有時它可以被用來在不適用C++調試的情況下學習虛擬機內部結構,有時也可以被拿來做性能監控和開發工具

私有變量: parkBlockerOffset

  • 被LockSupport的setBlocker和getBlocker調用
  • 這個對象是用來記錄線程被阻塞時被誰阻塞的,用於線程監控和分析工具來定位原因

LockSupport.java

/**
 * 通過反射機制獲取Thread類的parkBlocker字段對象。然後通過   
 * sun.misc.Unsafe對象的objectFieldOffset方法獲取到
 * parkBlocker在內存裏的偏移量
 */
parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));

Thread.java

    /**
     * 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;

JVM的實現可以自由選擇如何實現Java對象的“佈局”,也就是在內存裏Java對象的各個部分放在哪裏,包括對象的實例字段和一些元數據等。
sun.misc.Unsafe裏關於對象字段訪問的方法把對象佈局抽象出來,它提供了objectFieldOffset()方法用於獲取某個字段相對 Java對象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之類的方法可以使用前面獲取的偏移量來訪問某個Java 對象的某個字段

爲什麼要用偏移量來獲取對象?不直接調用get/set方法?

parkBlocker就是在線程處於阻塞的情況下才會被賦值。線程都已經阻塞了,如果不通過這種內存的方法,而是直接調用線程內的方法,線程是不會迴應調用的


LockSupport Method

  • LockSupport中有且只有一個私有方法
    • 對給定線程t的parkBlocker賦值。爲了防止這個parkBlocker被誤用,該方法是不對外公開的
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對象,即返回的是阻塞線程t的Blocker對象
public static Object getBlocker(Thread t) {  
    if (t == null)  
        throw new NullPointerException();  
    return unsafe.getObjectVolatile(t, parkBlockerOffset);  
}  

以park開頭的方法,用於阻塞線程

帶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方法

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的賦值操作

以unpark開頭的方法,用於解除阻塞

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
}

park與unpark命令是成對出現的。unpark必須要在park命令後執行。但是線程的恢復並不一定要用unpark, 因爲park的時間參數,有些情況下線程會自己恢復

源碼參考

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