LockSupport
package com.fzkj.juc.locksupport;
import java.util.concurrent.locks.LockSupport;
/**
* @DESCRIPTION
*/
public class LockSupportDemo {
/**
* LockSupport 采用发放凭证的方式来阻塞线程(凭证最多为1),park方法一次会消耗掉1个凭证,unpark一次发放一个凭证(最多为1)。
* 当凭证为0的时候,线程会阻塞,直到有凭证。
* @param args
*/
public static void main(String[] args) {
DoubleUnparkAndPark();
}
// 两次阻塞、两次唤醒
private static void DoubleUnparkAndPark() {
Thread a = new Thread(() -> {
System.out.println("------ 进入a线程 ------");
LockSupport.park(); // 阻塞 和之前的 wait方法作用类似
LockSupport.park(); // 阻塞 和之前的 wait方法作用类似
System.out.println("------ a线程被唤醒 ------");
});
a.start();
Thread b = new Thread(() -> {
LockSupport.unpark(a);
LockSupport.unpark(a);
System.out.println("------ 唤醒线程 ------");
});
b.start();
}
// 先唤醒 再 阻塞
private static void UnparkBeforePark() {
Thread a = new Thread(() -> {
System.out.println("------ 进入a线程 ------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park(); // 阻塞 和之前的 wait方法作用类似
System.out.println("------ a线程被唤醒 ------");
});
a.start();
Thread b = new Thread(() -> {
LockSupport.unpark(a);
System.out.println("------ 唤醒线程 ------");
});
b.start();
}
// 常规多线程流程:阻塞 -> 唤醒
private static void LockSupportSimple() {
Thread a = new Thread(() -> {
System.out.println("------ 进入a线程 ------");
LockSupport.park(); // 阻塞 和之前的 wait方法作用类似
System.out.println("------ a线程被唤醒 ------");
});
a.start();
Thread b = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(a);
System.out.println("------ 唤醒线程 ------");
});
b.start();
}
}
由于基于凭证的方式来达到线程阻塞的目的。LockSupport
并不需要在同步代码块中才能用(wait/notify、await/signal需要在同步代码块中才能使用,而且阻塞和唤醒的顺序不能反,否则会死锁)。
相关面试题
为什么可以先唤醒线程,后阻塞线程?
LockSupport
之所以会阻塞线程,就是因为park
方法会等待一个凭证,默认值是0,代表没有凭证,而 unpark
方法会发放一个凭证,之后调用park
方法的时候直接消耗凭证,就不会阻塞。
为什么唤醒两次再阻塞两次,最终结果还是会阻塞?(两次unpark、两次park)
因为凭证最多只能是1。两次unpark后的凭证还是1,而两次park中第一次正常,第二次由于没有凭证就会阻塞。