LockSupport是JUC提供的一个线程阻塞与唤醒的工具类,该工具类可以让线程在任意位置阻塞和唤醒,其所有的方法都是静态方法。
1. LockSupport的常用方法
public class LockSupport {
public static void park();
public static void unpark(Thread thread);
public static void parkNanos(Object blocker, long nanos);
public static void parkUntil(Object blocker, long deadline);
public static void park(Object blocker);
public static void parkNanos(Object blocker, long nanos);
public static Object getBlocker(Thread t);
}
下面是一个简单的通过LockSupport阻塞和唤醒线程的演示实例:
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("t1线程即将进入无限阻塞...");
LockSupport.park();
if (Thread.currentThread().isInterrupted()) {
System.out.println("t1被中断了,仍然继续运行...");
} else {
System.out.println("t1被重新唤醒了...");
}
}, "t1");
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(() -> {
System.out.println("t2线程即将进入无限阻塞...");
LockSupport.park();
if (Thread.currentThread().isInterrupted()) {
System.out.println("t2被中断了,仍然继续运行...");
} else {
System.out.println("t2被重新唤醒了...");
}
}, "t2");
t2.start();
Thread.sleep(1000);
t1.interrupt();
LockSupport.unpark(t2);
}
}
执行结果:
t1线程即将进入无限阻塞...
t2线程即将进入无限阻塞...
t1被中断了,仍然继续运行...
t2被重新唤醒了...
2. LockSupport.park()和Thread.sleep()的区别
从功能上说,LockSupport.park()与Thread.sleep()方法类似,都是让线程阻塞,二者的区别如下:
(1)Thread.sleep()没法从外部唤醒,只能自己醒过来;而被LockSupport.park()方法阻塞的线程可以通过调用LockSupport.unpark()方法去唤醒。
(2)Thread.sleep()方法声明了InterruptedException中断异常,这是一个受检异常,调用者需要捕获这个异常或者再抛出;而调用LockSupport.park()方法时不需要捕获中断异常。
(3)被LockSupport.park()方法、Thread.sleep()方法所阻塞的线程有一个特点,当被阻塞线程的Thread.interrupt()方法被调用时,被阻塞线程的中断标志将被设置,该线程将被唤醒。不同的是,二者对中断信号的响应方式不同:LockSupport.park()方法不会抛出InterruptedException异常,仅仅设置了线程的中断标志;而Thread.sleep()方法会抛出InterruptedException异常。
(4)与Thread.sleep()相比,调用LockSupport.park()能更精准、更加灵活地阻塞、唤醒指定线程。
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1"){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("t1线程被打断,抛出InterruptedException异常,被catch捕获");
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(500);
t1.interrupt();
}
}
通过Thread.sleep()方法进入阻塞的线程不会释放持有的锁,因此在持有锁的时候调用该方法需要谨慎。那么通过LockSupport.park()方法进入阻塞的线程,会不会释放所持有的锁呢?当然也不会。
3. LockSupport.park()与Object.wait()的区别
从功能上说,LockSupport.park()与Object.wait()方法也类似,都是让线程阻塞,二者的区别如下:
(1)Object.wait()方法需要在synchronized块中执行,而LockSupport.park()可以在任意地方执行。
(2)当被阻塞线程被中断时,Object.wait()方法抛出了中断异常,调用者需要捕获或者再抛出;当被阻塞线程被中断时,LockSupport.park()不会抛出异常,调用时不需要处理中断异常。
(3)如果线程在没有被Object.wait()阻塞之前被Object.notify()唤醒,也就是说在Object.wait()执行之前去执行Object.notify(),就会抛出IllegalMonitorStateException异常,是不被允许的;而线程在没有被LockSupport.park()阻塞之前被LockSupport.unPark()唤醒,也就是说在LockSupport.park()执行之前去执行LockSupport.unPark(),不会抛出任何异常,是被允许的。
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1线程即将进入无限阻塞...");
LockSupport.park();
if (Thread.currentThread().isInterrupted()) {
System.out.println("t1被中断了,仍然继续运行...");
} else {
System.out.println("t1被重新唤醒了...");
}
}, "t1");
t1.start();
LockSupport.unpark(t1);
LockSupport.unpark(t1);
Thread.sleep(2000);
LockSupport.unpark(t1);
}
}
执行结果:
t1线程即将进入无限阻塞...
t1被重新唤醒了...
通过结果可以看出,前两次LockSupport.unpark(t1)唤醒操作没有发生任何作用,因为线程t1还没有被LockSupport.park()阻塞。只有在被LockSupport.park()阻塞之后,LockSupport.unpark(t1)唤醒操作才能将线程t1唤醒。
|