LockSupport使用及源码详解

Posted 叶长风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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工具用途和原理还是比较简单。

以上是关于LockSupport使用及源码详解的主要内容,如果未能解决你的问题,请参考以下文章

Java并发:挂起与唤醒线程LockSupport工具类详解

jdk源码之LockSupport

JDK源码那些事儿之LockSupport

JDK源码那些事儿之LockSupport

LockSupport源码分析

面试 LockSupport.park()会释放锁资源吗?