LockSupport是挂起线程的一种实现方式,它不是同步锁,只是一个提供给同步锁调用,把当前线程挂起的协助类。它也能够单独使用,把某个线程挂起。
一、方法介绍
LockSupport的方法有以下几个,后面会一个个介绍

1.挂起方法
1.1 park()
这个方法很简答,直接调用了UNSAFE类的原生方法,可以立即它会立刻把当前线程挂起。注意它是一个静态方法,直接通过类名引用就可以调用
public static void park() {
UNSAFE.park(false, 0L);
}
// UNSAFE类里的方法
public native void park(boolean var1, long var2);
1.2 park(Object blocker)
跟无参的park相比,传入的对象可以记录在Thread的parkBlocker变量中,当调用getBlocker方法时,可以获取到这个传入的对象。
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
Class<?> tk = Thread.class;
private static final long parkBlockerOffset; = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
1.3 parkUntil(Object blocker, long deadline)
这个方法,可以挂起线程直到某个时间点,注意deadline是绝对时间,即未来的某个时间点的时间戳
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
2.解除挂起方法
2.1 正常解除挂起
解除挂起只有一个方法,直接调用UNSAFE的方法。
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
2.2 被其他线程干扰
当其他线程调用了挂起线程的interrupt()方法,也会把挂起线程唤醒
2.3 其他未知原因
挂起的线程也会有未知的意外会被唤醒,所以如果我们对这个挂起操作比较依赖时,要把挂起方法放到轮询的方法里,比如:
while(挂起条件成立){
LockSupport.park();
}
二、使用实例
如下例子中,我开启了两个线程去调用同步锁方法,当获取锁后调用park方法,看看它被挂起后会不会释放锁。从例子可以看出,park方法挂起时是不会释放锁的,这点跟Object的wait方法不一样,wait方法会释放锁。
import java.util.concurrent.locks.LockSupport;
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
LockSupportTest test = new LockSupportTest();
Thread t1 = new Thread(() -> {
System.out.println("线程1启动了");
test.lock("线程1");
});
t1.start();
Thread.sleep(1 * 1000);
Thread t2 =new Thread(() -> {
System.out.println("线程2启动了");
test.lock("线程2");
});
t2.start();
Thread.sleep(1 * 1000);
LockSupport.unpark(t1);
Thread.sleep(1 * 1000);
LockSupport.unpark(t2);
}
public synchronized void lock(String threadName){
System.out.println("加锁开始,线程名=" + threadName);
LockSupport.park();
System.out.println("加锁结束,线程名=" + threadName);
}
}

wait方法释放锁的例子如下,注意不管是wait还是notifyAll方法调用前都要首先获得同步锁。
LockSupportTest test = new LockSupportTest();
Thread t1 = new Thread(() -> {
System.out.println("线程1启动了");
synchronized (test){
while (flag == 0){
try {
test.wait();
} catch (InterruptedException e) {
}
}
}
System.out.println("线程1结束了");
});
t1.start();
Thread.sleep(1 * 1000);
Thread t2 =new Thread(() -> {
System.out.println("线程2启动了");
synchronized (test){
while (flag == 0){
try {
test.wait();
} catch (InterruptedException e) {
}
}
}
System.out.println("线程2结束了");
});
t2.start();
Thread.sleep(2000);
// 更新等待标志位,被唤醒的线程才会退出
flag = 1;
synchronized (test){
test.notifyAll();
}
t1.join();
t2.join();

三、总结
- LockSupport使用场景是挂起线程,如果线程占有锁,挂起时不会释放锁。它与Object的wait方法的区别就这在于这里,wait方法会释放锁。
- LockSupport的park和unpark方法调用上没有顺序的要求,也就是说可以先调用,unpark再调用park。可以把它理解为古代的虎符,成对出现时才可以调兵调兵遣将。park和unpark成对出现时线程就可以继续运行。但是不能多次调用unpark,然后试图多次调用park来让线程运行。比如下面这样的代码,
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
// park1
LockSupport.park();
// park2 程序最终会在park2这里挂起
LockSupport.park();
// park3
LockSupport.park();
本文深入解析LockSupport类在Java并发编程中的应用,包括其挂起和解除挂起方法,以及与Object.wait方法的区别。通过实例演示如何使用LockSupport进行线程控制。
2万+

被折叠的 条评论
为什么被折叠?



