一.概述
LockSupport 和 CAS 是Java并发包中很多并发工具控制机制的基础,它们底层其实都是依赖Unsafe实现。
LockSupport是创建锁和其他同步类的基本线程阻塞原语,park 和 unpark 方法提供了阻塞和解除阻塞线程的有效方法
LockSupport类以及每个使用它的线程和一个permit({@link java.util.concurrent.Semaphore})相关联:
- 如果permit许可可用, permit最多一个, permit相当于0、1开关,默认是0
- 调用一次unpark()permit就+1,permit变成1,再次调用unpark()多次不会累积,即调用多次permit任然为1
- 调用一次 park() permit就-1,permit变成0,再次调用 park() 线程就会阻塞(因为没有1了,会等在这儿,直到有1)
二.LockSupport类讲解
1. 私有构造函数
构造函数私有,说明LockSupport不能初始化
private LockSupport() {}
2. 静态代码块
- UNSAFE:可以直接操控内存,被JDK广泛用于自己的包中,但是不建议在生产环境中使用这个类。因为这个API十分不安全、不轻便、而且不稳定
- UNSAFE.objectFieldOffset(Field field)可以准确获取某个字段相对于对象的起始内存地址的字节偏移量
- parkBlocker是Thread类中的成员变量,记录了当前线程阻塞时是被谁阻塞的,用于线程监控和分析,这里利用反射获取Thread类中的parkBlocker字段信息
private static final sun.misc.Unsafe UNSAFE;
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. 设置/获取blocker对象
- parkBlockerOffset是Threa中parkBlocker字段的偏移量,在设置/获取parkBlocker会用到
- parkBlocker只有在线程被阻塞时才会被赋值,由于线程被阻塞了,需要通过这种内存方式获取,不能直接调用Thread中get方法获取,线程是不会回应调用的
/**
* 设置当前线程被阻塞时是被谁阻塞的,blocker用于线程监控和分析
* @param t 当前被阻塞线程
* @param arg 阻塞当前线程的对象
* */
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
4. 阻塞/解除阻塞线程方法
4.1 解除阻塞unpark
- unpark(Thread thread):解除阻塞
如果给定线程thread的许可permit尚不可用,使其可用,permit会加1,但是不会累加
如果给定线程thread是被阻塞,则解除其阻塞状态
/**
* 解除线程thread的阻塞
* @param thread 阻塞的线程
* */
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
4.2 阻塞线程park
- park() : 阻塞当前线程
- park(Object blocker)
- parkNanos(long nanos):阻塞当前线程,最长阻塞nanos纳秒
- parkNanos(Object blocker, long nanos)
- parkUntil(long deadline):阻塞当前线程到指定时间deadline
- parkUntil(Object blocker, long deadline)
- 如果许可permit可用(即permit>0),则使用许可(permit-1),并且调用立即返回
- 如果许可permit不可用,阻塞当前线程
- blocker参数表示线程被谁阻塞,用于线程监控和分析,建议使用带blocker参数的方法,而不是不带blocker参数的原始方法,在锁实现中blocker参数一般为this
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);
}
/**
* 阻塞当前线程,线程将一直阻塞直到超时或者中断等条件出现
* 1. 记录当前线程被blocker阻塞
* 2. 阻塞当前线程
* 3. 线程t解除阻塞后清除blocker
* */
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
/**
* 阻塞当前线程,最长等待时间不超过nanos纳秒
*
* @param blocker
* @param nanos 等待纳秒
* */
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);
}
4.3 唤醒线程
线程调用LockSupport.park(this)被挂起,下面三种情况会唤醒线程:
1. 其他线程中以被挂起线程为目标调用unpark
2. 其他线程中中断当前就线程
3. 虚假呼叫,即无理由返回
三. 示例说明
1. 阻塞线程
package com.test;
import java.util.concurrent.locks.LockSupport;
public class TestPart {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
public void run() {
System.out.println();
System.out.println("子线程开始执行:");
System.out.println("子线程开始阻塞");
LockSupport.park(); // 阻塞子线程
System.out.println();
System.out.println("子线程执行完成");
}
};
System.out.println("主线程开始执行");
thread.start();
System.out.println("主线程阻塞5秒");
Thread.sleep(5000);
System.out.println();
System.out.println("主线程睡眠完成");
System.out.println();
System.out.println("子线程解除阻塞");
LockSupport.unpark(thread); // 子线程解除阻塞
System.out.println();
System.out.println("主线程执行完成");
}
}
执行结果:
2. 阻塞线程最多等待纳秒
package com.test;
import java.util.concurrent.locks.LockSupport;
/**
* 阻塞当前线程,是否真的阻塞取决于permit是否有效
* 1. 每个线程都有个相关的permit(在{@link java.util.concurrent.Semaphore}类中)
* 2. 如果许可证可用, permit最多一个, permit相当于0、1开关,默认是0
* 3. 调用一次unpark()permit就+1,permit变成1,再次调用unpark()多次不会累积,即调用多次permit任然为1
* 4. 调用一次 park() permit就-1,permit变成0,再次调用 park() 线程就会阻塞(因为没有1了,会等在这儿,直到有1)
* */
public class TestPartNanos {
public static void main(String[] args) {
/**
* 1. 当前permit = 0
* 2. parkNanos: 没有1可减,阻塞
* 3. 等待5秒后才继续运行
* */
System.out.println("开始parkNanos: " + System.currentTimeMillis());
LockSupport.parkNanos(5000000000L); // 当前线程阻塞5秒
System.out.println("结束parkNanos: " + System.currentTimeMillis());
System.out.println();
/**
* 1. 当前permit = 0
* 2. unpark: permit = 1
* 3. parkNanos : permit = 0, 不会阻塞,继续运行
* */
System.out.println("开始parkNanos: " + System.currentTimeMillis());
LockSupport.unpark(Thread.currentThread());
LockSupport.parkNanos(5000000000L);
System.out.println("结束parkNanos: " + System.currentTimeMillis());
System.out.println();
/**
* 1. 当前permit = 0
* 2. unpark: permit = 1
* 3. unpark: permit = 1, 多次调用unpark不会累积
* 4. parkNanos : permit = 0
* 5. parkNanos : 没有1可减,阻塞
* */
System.out.println("开始parkNanos: " + System.currentTimeMillis());
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
LockSupport.parkNanos(5000000000L);
System.out.println("首次parkNanos: " + System.currentTimeMillis());
LockSupport.parkNanos(5000000000L);
System.out.println("结束parkNanos: " + System.currentTimeMillis());
}
}
执行结果:
3. 阻塞线程直到指定时间
package com.test;
import java.util.Date;
import java.util.concurrent.locks.LockSupport;
/**
* parkUntil(long deadline):将当前线程一直阻塞到指定的时间
* */
public class TestPartUntil {
public static void main(String[] args) {
Long start = System.currentTimeMillis();
System.out.println("开始parkUntil : " + start + "L, 当前时间:" + (new Date(start)));
System.out.println();
Long deadline = 1524737607772L;
System.out.println("线程一直阻塞到 : " + deadline + "L, 阻塞时间:"+ (new Date(deadline)));
LockSupport.parkUntil(deadline);
System.out.println();
Long end = System.currentTimeMillis();
System.out.println("结束parkUntil : " + end + "L, 当前时间:" + (new Date(end)));
}
}
执行结果:
四. 总结
- park 和 unpark 方法提供了阻塞和解除阻塞线程的有效方法
- 由于permit的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性
- 如果许可permit可用,调用part会使用permit,并且调用立即返回
- 如果许可permit不可用,调用part会阻塞线程
- part方法不会报告什么造成了此方法的返回,所以调用者应该重新检查最先导致线程暂停的条件。例如调用者可以确定线程的中断状态,或返回时的当前时间
五. LockSupport源码
package java.util.concurrent.locks;
import sun.misc.Unsafe;
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
/**
* UNSAFE:可以直接操控内存,被JDK广泛用于自己的包中,但是不建议在生产环境中使用这个类。因为这个API十分不安全、不轻便、而且不稳定
* UNSAFE.objectFieldOffset(Field field):可以准确获取某个字段相对于对象的起始内存地址的字节偏移量
* parkBlocker:是Thread类中的成员变量,记录了当前线程阻塞时是被谁阻塞的,用于线程监控和分析,这里利用反射获取Thread类中的parkBlocker字段信息
* */
private static final sun.misc.Unsafe UNSAFE;
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); }
}
/**
* 设置当前线程被阻塞时是被谁阻塞的,blocker用于线程监控和分析
* @param t 当前被阻塞线程
* @param arg 阻塞当前线程的对象
* */
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
/**
* 解除线程thread的阻塞
* @param thread 阻塞的线程
* */
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
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);
}
/**
* 阻塞当前线程,线程将一直阻塞直到超时或者中断等条件出现
* 1. 记录当前线程被blocker阻塞
* 2. 阻塞当前线程
* 3. 线程t解除阻塞后清除blocker
* */
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
/**
* 阻塞当前线程,最长等待时间不超过nanos纳秒
*
* @param blocker
* @param nanos 等待纳秒
* */
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);
}
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;
}
}