LockSupport的源码分析(中)

来源:互联网 发布:mac下创建maven 编辑:程序博客网 时间:2024/05/12 03:47

一、介绍

要学习JAVA中是如何实现线程间的锁,就得从LockSupport这个类先说起,因为这个类实现了底层的一些方法,各种的锁实现都是这个基础上发展而来的。这个类方法很少,但理解起来需要花费一点时间。

源码如下:

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);    }}
这个类提供的都是静态方法,且无法被实例化。在LockSupport中有两个私有的成员变量:

private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long parkBlockerOffset;
我们都知道JAVA语言是平台无关的,一次编译,可以在任何平台上运行,但是真的不可以调用一些平台相关的方法吗?其实unsafe类是可以做到调用平台相关的方法的。
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调用。这个对象是用来记录线程被阻塞时是被谁阻塞的。用于线程监控和分析工具来定位原因的。主要调用了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) {        // 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) {        return unsafe.getObjectVolatile(t, parkBlockerOffset);}
参数:Thread t, 被操作的线程对象
返回:parkBlocker对象
解读:从线程t中获取他的parkerBlocker对象。这个方法是对外公开的。

二、park和unpark

 /**           * 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的时候会一直阻塞着),解除线程阻塞

(4). 当absolute是true时,如果给定的时间是过去了的或者伪造的(在我理解是参数不合法时),解除线程阻塞。这个操作是不安全的。
理解一下,park与unpark命令一般是成对出现的。但是线程的恢复并不一定要用unpark, 因为park的时间参数,有些情况下线程会自己恢复。

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

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

三、park方法源码

我们继续看一下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") ;         }     }

0 0
原创粉丝点击