java.util.concurrent.locks.LockSupport 详解

要学习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 APIprivate 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调用。查看JAVADOC会发现这么一段解释:
在这里插入图片描述大致意思是,这个对象是用来记录线程被阻塞时被谁阻塞的。用于线程监控和分析工具来定位原因的。主要调用了LockSupport的getBlocker方法。

原来,parkBlocker是用于记录线程是被谁阻塞的。可以通过LockSupport的getBlocker获取到阻塞的对象。用于监控和分析线程用的。

问题2:偏移量又是做什么的?

private static void setBlocker(Thread t, Object arg)public static Object getBlocker(Thread t) {return unsafe.getObjectVolatile(t, parkBlockerOffset);}

参数:

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是做什么的.

/**  
* Unblock the given thread blocked on park, or, if it is
* not blocked, cause the subsequent call to park 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
* unpark occurs, or a balancing unpark 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 unpark 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);}

参数:

1、Object blocker:用于记录到线程中的parkBlocker对象。2、nanos:在nanos时间后线程自动恢复挂起3、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 interruptedif (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") ;}}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部