一个很灵活的线程工具类LockSupport
LockSupport是一个编程工具类,主要是为了阻塞和唤醒线程用的。使用它我们可以实现很多功能。
LockSupport简介
LockSupport是什么
LockSupport是一个线程工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,也可以在任意位置唤醒。
它的内部其实两类主要的方法:park(停车阻塞线程)和unpark(启动唤醒线程)。
注意上面的123方法,都有一个blocker,这个blocker是用来记录线程被阻塞是被谁阻塞的。用于线程监控和分析工具来定位原因的。
现在我们知道了LockSupport是用来阻塞和唤醒线程的,而且之前相信我们都知道wait/notify也是用来阻塞和唤醒线程的,那么它相比,LockSupport有什么优点呢?
与wait/notify对比
我们先来举一个使用案例:
public class LockSupportTest {
static class MyThread extends Thread{
@Override
public void run() {
System.out.println("进入线程");
LockSupport.park();
System.out.println("t1线程运行结束");
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
System.out.println("t1线程启动了,但是内部进行了park");
LockSupport.unpark(t1);
System.out.println("LockSupport进行了unpark");
}
}
上面这段代码的意思是,我们定义一个线程,但是在内部进行了park,因此需要unpark才能唤醒继续执行,不过上面,我们在MyThread进行的park,在main线程进行的unpark。
这样来看,好像和wait/notify没有什么区别。那它们的区别到底是什么呢?这个就需要仔细的观察了。这里主要有两点:
(1)wait和notify都是Object中的方法,在调用这两个方法前必须先获得锁对象,但是park不需要获取某个对象的锁就可以锁住线程。
(2)notify只能随机选择一个线程唤醒,无法唤醒指定的线程,但是unpark可以唤醒一个指定的线程。
前面我们介绍了线程一共有六种状态,而park系列方法线程进入两种状态:WAITING等待状态或TIMED_WAITING等待状态。这两种状态都会使线程阻塞在当前位置。
那么怎么唤醒这两种状态的线程呢?
对于WAITING等待状态有两种唤醒方式:
- 调用对应的唤醒方法。这里就是LockSupport的unpark方法。
- 调用该线程变量的interrupt()方法,会唤醒该线程,并抛出InterruptedException异常。
对于TIMED_WAITING等待状态来说,它比WAITING状态多了一种唤醒方式,就是超过规定时间,那么线程会自动醒来。
LockSupport使用
public class LockSupportDemo {
public static void main(String[] args) {
//获取当前线程
final Thread currentThread = Thread.currentThread();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//睡眠5秒,等待主线程调用park
Thread.sleep(5000);
System.out.println("子线程进行unpark操作!");
// 进行唤醒给定的currentThread线程
LockSupport.unpark(currentThread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
System.out.println("开始阻塞!");
// 进行阻塞给定的currentThread线程
LockSupport.park(currentThread);
System.out.println("结束阻塞!");
}
}
public class LockSupportDemo {
public static void main(String[] args) {
//获取当前线程
final Thread currentThread = Thread.currentThread();
//在park之前先进行一次unpark
LockSupport.unpark(currentThread);
System.out.println("开始阻塞!");
// 由于在park之前进行了一次unpark,所以会抵掉本次的park操作。因而不会阻塞在此处
LockSupport.park(currentThread);
System.out.println("结束阻塞!");
}
}