Java中的LockSupport的使用
1、LockSupport方法介绍
在Java多线程中,当需要阻塞或唤醒一个线程时,我们可以使用LockSupport工具类来完成相应的功能。而LockSupport也成为构成同步器的重要工具。AbstractQueuedSynchronizer就是通过LockSupport来实现线程阻塞和唤醒的。
下面我们分别介绍一下它的主要方法。
方法 | 作用 |
---|---|
void unpark(Thread thread) | 唤醒处于阻塞状态的线程 |
void park() | 阻塞当前线程,调用unpark(Thread thread) 唤醒该线程 |
void parkNanos(long nanos) | 阻塞当前线程,阻塞时间不超过nanos纳秒 |
void parkUntil(long deadline) | 阻塞当前线程,直到deadline时间点,deadline时间点以System.currentTimeMillis()为参考 |
void park(Object blocker) | 与park()相同, blocker标识等待对象 |
void parkNanos(Object blocker, long nanos) | 与parkNanos(long nanos)相同, blocker标识等待对象 |
void parkUntil(Object blocker, long deadline) | 与parkUntil(long deadline) 相同, blocker标识等待对象 |
对比park() 与park(Object blocker) 的区别
下面一段代码,分别调用了park()和park(Object blocker)方法,使其对应线程阻塞,随后我们查看其线程阻塞情况。
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
LockSupport.park();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
LockSupport.park(new Object());
}
});
t1.start();
t2.start();
}
这里利用jps命令先找出执行代码对应pid,然后通过jstack命令打出thread dump信息。
随后我们对比两个阻塞方法之间的差别,使用红色矩形框起来以方便查看。很明显的缺少了一行 - parking to wait for 标识等待的对象。这个对象主要用于问题排查和系统监控。
parkUntil(long deadline) 方法的时间点在哪里
刚开始我也不是很清楚这个时间点在哪里,但看到时间点的类型为long,就想到System.currentTimeMillis()的时间类型也为long,于是就出现下面代码了。
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println("start time:" + start);
// 以System.currentTimeMillis()的时间点为开始时间
LockSupport.parkUntil(System.currentTimeMillis() + 1000);
System.out.println("park time:" + (System.currentTimeMillis() - start) + " ms");
}
看到运行结果后,印证了我的猜测,时间点是以System.currentTimeMillis()为基线的。可能我表达不够准确,最好还是参考代码帮助理解。
2、LockSupport源码及注释
UNSAFE.park(boolean var1, long var2),该方法有两个参数。
第一个参数是是否是绝对时间,第二个参数是等待时间值。如果isAbsolute是true则会实现ms定时。如果isAbsolute是false则会实现ns定时。
public class LockSupport {
// 私有化构造函数,禁止创建实例
private 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);
}
/**
* 唤醒处于阻塞状态的线程
*/
public static void unpark(Thread thread) {
// 调用UNSAFE类
if (thread != null)
UNSAFE.unpark(thread);
}
/**
* 在blocker对象上进行阻塞
* @since 1.6
*/
public static void park(Object blocker) {
Thread t = Thread.currentThread();
// 为当前线程设置阻塞对象
setBlocker(t, blocker);
// 阻塞
UNSAFE.park(false, 0L);
// 阻塞被唤醒,释放线程的阻塞对象。
setBlocker(t, null);
}
/**
* @since 1.6
*/
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);
}
}
/**
* @since 1.6
*/
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
// 阻塞,并设置绝对超时时间
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
/**
*
* @since 1.6
*/
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
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);
}
/**
* 该方法只在StampedLock类中用到,暂时不作讨论
*/
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
UNSAFE.putInt(t, SECONDARY, r);
return r;
}
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
// 获取属性在对应类中的Offset位置
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
3、关于sun.misc.Unsafe类型
我们在阅读Java并发包中多个类的源码时都遇到这个类的使用,类中大多都是native方法,主要用于执行低级别、不安全的方法。我会和大家一起来理解方法的作用,不会深入讨论Unsafe方法的实现,若读者感兴趣可以自行寻找相关文章。