LockSupport
LocalSupport类特性
- 不可实例化
private LockSupport() {} // Cannot be instantiated.
LockSupport的方法都是静态方法
私有变量
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
私有变量:unsafe
- sun.misc.Unsafe可以直接操控内存。被JDK广泛用于自己的包中,如java.nio和java.util.concurrent
- API十分不安全、不轻便、而且不稳定。不建议在生产环境中使用
- 这个不安全的类提供了一个观察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的时间参数,有些情况下线程会自己恢复