版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u010647035/article/details/85039543
1、LockSupport 概述
LockSupport 定义了一组以 park 开头的方法,用来阻塞当前线程,用 ( Thread thread) 方法来唤醒一个被阻塞的线程。
每个线程都有一个许可(permit),permit 有两个值1和0,默认是0。
当调用unpark(thread)方法,就会将thread线程的许可permit设置成1(多次调用unpark方法,不会累加,permit值还是1)。
当调用park()方法,如果当前线程的permit是1,那么将permit设置为0,并立即返回。如果当前线程的permit是0,那么当前线程就会阻塞,直到别的线程将当前线程的permit设置为1。park方法会将permit再次设置为0,并返回。
因为permit默认是0,所以一开始调用park()方法,线程会被阻塞。调用unpark(thread)方法后,会自动唤醒thread线程。
2、LockSupport 与 Object 对象的 wait/notify/notifyAll
在没有LockSupport之前,线程的挂起和唤醒咱们都是通过Object的wait和notify/notifyAll方法实现,我们先看一个实例:
线程1 执行一段计算逻辑后调用wait阻塞自己。最后主线程调用notify方法唤醒线程1,线程1然后打印自己执行的结果。
public static void main(String[] args) throws Exception {
final Object obj = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
try {
synchronized (obj) {
obj.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(sum);
}
});
thread1.start();
Thread.sleep(1000);
synchronized (obj) {
obj.notify();
}
}
下面再看一下使用 LockSupport 的实现,很显然简洁了很多,直接调用即可,不必一定要放在同步块中
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
//阻塞当前线程
LockSupport.park();
System.out.println(sum);
}
});
thread1.start();
Thread.sleep(1000);
//唤醒指定线程
LockSupport.unpark(thread1);
}
与Object的wait/notify 相比,LockSupport 有两个优势:
1、LockSupport不需要在同步代码块里,所以线程间也不需要维护一个共享的同步对象就实现了线程间的解耦。
2、unpark 函数可以先于park调用,所以不需要担心线程间的执行顺序。
3、LockSupport
/**
* 提供阻塞线程和唤醒线程的方法。
*/
public class LockSupport {
// 私有构造函数,不能在外部实例化
private LockSupport() {}
// 用来设置线程t的parkBlocker属性。此对象在线程受阻塞时被记录,以允许监视工具和诊断工具确定线程受阻塞的原因。
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
// 唤醒处于阻塞状态下的thread线程
public static void unpark(Thread thread) {
// 当线程不为null时调用
if (thread != null)
// 通过UNSAFE的unpark唤醒被阻塞的线程
UNSAFE.unpark(thread);
}
// 阻塞当前线程
public static void park(Object blocker) {
Thread t = Thread.currentThread();
// 设置线程t的parkBlocker属性,用于记录线程阻塞情况
setBlocker(t, blocker);
// 通过UNSAFE的park方法阻塞线程
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
// 阻塞当前线程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);
}
}
// 阻塞当前线程,超过deadline日期线程就会被唤醒返回
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
// 获取线程t的parkBlocker属性
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);
}
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 = ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
UNSAFE.putInt(t, SECONDARY, r);
return r;
}
//unsafe 用来实现底层操作
private static final 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 = 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); }
}
}