LockSupport使用及源碼詳解

LockSupport使用及源碼詳解

在講了一批併發工具後,突然想起LockSupport類了,這個工具類我也很少用,所以這次寫了個小demo後,決定也寫一篇文章記錄一下,以下就是記錄過程。

LockSupport用法


在網上隨便找了個演示demo,如下:

public class LockSupportDemo {

    public static void main(String[] args) {
        Person person = new Person();
        Thread t = new Thread(() -> {
            person.walk();
        }, "jason");

        try {
            t.start();
            Thread.sleep(3000);
            System.out.println("3s 過去了");
            //解救該線程
            LockSupport.unpark(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Person {

    public void walk() {
        Thread currentThread = Thread.currentThread();
        System.out.println(currentThread.getName() + "在走。。。。前面有人擋住了");
        LockSupport.park();//阻塞當前線程
        System.out.println(currentThread.getName() + "又可以走了");
    }
}

運行結果也比較有意思,在Person中的wark方法中,在LockSupport阻塞了當前線程後,線程運行到該出就不往下執行了。

System.out.println(currentThread.getName() + "在走。。。。前面有人擋住了");
        LockSupport.park();//阻塞當前線程

在主程序對該線程解鎖後,該線程才繼續向下執行。

//解救該線程
            LockSupport.unpark(t);

LockSupport功能可以說用法就比較明確了,可以任何時候阻塞一個線程,也可以隨時恢復一個線程的執行,和wait/notify的功能有點類似。

LockSupport源碼


LockSupport用法比較簡單,現在進來看看源碼實現。進入到pack方法中。

    public static void park() {
        UNSAFE.park(false, 0L);
    }

public native void unpark(Object var1);
public native void park(boolean var1, long var2);

UNSAFE類就不看了,這個類提供的相當多的操作是native方法,pack和unpack方法也是,真正的實現是調用的系統底層的實現。

可以看下unpack操作,也是相同的。

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

這兩個方法比較簡單,但是需要關注下LockSupport中的一個私有方法。

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類中的一個變量。

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

註釋上就是說這個對象可以被LockSupport中的setBlocker與getBlocker調用,但是這個對象有什麼用呢。

這個作用可以看LockSupport中的一段註釋。

* <p>The three forms of {@code park} each also support a
 * {@code blocker} object parameter. This object is recorded while
 * the thread is blocked to permit monitoring and diagnostic tools to
 * identify the reasons that threads are blocked. (Such tools may
 * access blockers using method {@link #getBlocker(Thread)}.)
 * The use of these forms rather than the original forms without this
 * parameter is strongly encouraged. The normal argument to supply as
 * a {@code blocker} within a lock implementation is {@code this}.
 *
 * <p>These methods are designed to be used as tools for creating
 * higher-level synchronization utilities, and are not in themselves
 * useful for most concurrency control applications.  The {@code park}
 * method is designed for use only in constructions of the form:

這段註釋詳細的說明了原因,這一段大意就是parkBlocker記錄了阻塞當前線程的對象是哪一個,以方便查詢問題時定位問題,主要用於監控與分析線程用的,同時也鼓勵這種方式來調用。

對於上述代碼。

UNSAFE.putObject(t, parkBlockerOffset, arg);

看下這裏的parkBlockerOffset的作用。

private static final long parkBlockerOffset;
parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));

parkBlockerOffset就是線程中的parkBlocker在內存中的偏移量,然後根據這個偏移量來獲取對象,在這爲什麼不採用get、set方法來獲取這個字段的原因是如果線程被阻塞住,是無法獲取線程中的字段的,因此只能通過這種偏移量的形式來拿到。

然後上面那個setBlocker方法爲私有方法的原因應該就是這個方法是隨着線程阻塞時進行調用的,如果沒有阻塞線程就調用setBlockers方法可能會造成其他問題,因此就把這個方法內置到pack方法中了。

public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

這裏設置了blocker,在park方法進行阻塞後,當阻塞結束後,將blocker同時置爲null。

在設置完blocker後,有getBlocker方法來獲取blocker,如下:

public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }

對於Unsafe中的pack和unpack方法找到兩個註釋,可以看下:

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

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

這兩個方法解釋了pack方法和unpack方法的用途,具體實現是看不到了,在原生c++代碼中。

LockSupport工具用途和原理還是比較簡單。

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