【JUC】 LockSupport类的park等待和unpark唤醒
文章目录
1. 概述
LockSuppot:用于创建锁和其他同步类的基本线程阻塞原语。
这个类与每个使用它的线程相关联,一个许可证(在Semaphore
类的意义上)。 如果许可证可用,则呼叫park
将park
返回,在此过程中消耗它; 否则可能会阻止。 致电unpark
使许可证可用,如果尚不可用。 (与信号量不同,许可证不能累积,最多只有一个。)
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能, 每个线程都有一个许可(permit),permit只有两个值1和零,默认是零。可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。
2. API解释
-
static void park()
:禁止当前线程进行线程调度(就是阻塞),除非许可证可用。permit默认是零,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为零并返回。
public static void park() { UNSAFE.park(false, 0L); }
-
static void unpark(Thread thread)
:为指定的线程提供许可证(前提是尚未提供过许可证)。调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park()方法会立即返回。
public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
3. 代码示例
3.1 先等待再唤醒
先等待再唤醒,这是一般情况。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t ----come in" + System.currentTimeMillis());
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "\t ----被唤醒" + System.currentTimeMillis());
}, "t1");
t1.start();
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); }
new Thread(() -> {
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
}, "t2").start();
}
运行结果如下:
3.2 先唤醒再等待
先唤醒再等待,这是特殊情况。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t ----come in" + System.currentTimeMillis());
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "\t ----被唤醒" + System.currentTimeMillis());
}, "t1");
t1.start();
new Thread(() -> {
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
}, "t2").start();
}
运行结果如下:
注:park和unpark必须成对出现。
4. 区别
除了LockSupport类的park、unpark方法可以实现等待唤醒功能,Object类的wait、notify以及Condition接口的await、signal也同样能实现。
不过LockSupport是它们俩的升级版本。
Object类和Condition接口的方法在使用上都有一定的限制条件。
4.1 wait和notify的限制
- wait和notify方法必须要在同步代码块中,且成对出现使用。
- 必须要先wait才能notify,否则会一直阻塞。
4.2 await和signal的限制
- Condition中的await和signal方法必须先获取锁。
- 一定要先await后signal,否则会一直阻塞。
而LockSupport类的方法就没有以上限制。