一、概览
LockSupport是Java线程阻塞原语,它是创建锁lock和其他同步类的基础。LockSupport为每个线程关联了一个许可证(信号量)。许可证的值为取值为0、1,初始值默认为0;LockSupport.park方法调用后,线程是否阻塞取决于当前线程信号量的值,若信号量为1,会立即执行;否则阻塞(若此时中断interrupt发生,则线程继续执行);LockSupport.unpark方法调用后,会将信号量的值修改为1,即放行线程。LockSupport可以解决因自旋花费大量时间的忙等待。

二、代码实例
2.1 LockSupport.unpark唤醒LockSupport.park
2.1.1 代码
public class LockBase {
public static void main(String[] args) {
final ParkThread parkThread = new ParkThread();
parkThread.start();
System.out.println(System.currentTimeMillis() + " :LockSupport.unpark");
LockSupport.unpark(parkThread);
}
private static void sleep(int secs) {
try {
TimeUnit.SECONDS.sleep(secs);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
}
static class ParkThread extends Thread{
@Override
public void run() {
System.out.println(System.currentTimeMillis() + " :ParkThread enter");
LockBase.sleep(5); // 等待5s确保主线程执行完 LockSupport.unpark(parkThread);
System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
LockSupport.park(); // 不会阻塞,因为上述语句[LockSupport.unpark(parkThread)]执行后,信号量为1。
System.out.println(System.currentTimeMillis() + ": ParkThread run");
}
}
}
2.1.2 运行结果

分析:
- 子线程执行
LockSupport.park并未阻塞,因为主线程在之前已经执行了LockSupport.unpark,使得子线程的许可证可用,即值为1;若不执行LockSupport.unpark,子线程将一直阻塞; LockSupport.unpark和LockSupport.park二者没有执行顺序限制,本质都是在操作信号量,前者写,后者读。LockSupport.unpark(Thread thread)可以指定线程。
2.2 中断interrupt唤醒LockSupport.park
2.2.1代码
public class LockBase {
public static void main(String[] args) {
final ParkThread parkThread = new ParkThread();
parkThread.start();
sleep(5);
System.out.println(System.currentTimeMillis() + " :parkThread.interrupt");
parkThread.interrupt(); // 中断线程
}
private static void sleep(int secs) {
try {
TimeUnit.SECONDS.sleep(secs);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
}
static class ParkThread extends Thread{
@Override
public void run() {
System.out.println(System.currentTimeMillis() + " :ParkThread enter");
System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
LockSupport.park(); // 阻塞,因为信号量为0。
System.out.println(System.currentTimeMillis() + ": ParkThread run");
}
}
}
2.2.2 运行结果

分析:
- 子线程执行
LockSupport.park后阻塞了,因为信号量初始值默认为0; - 子线程被中断
interrupt后,线程不再阻塞了,开始往下执行了。即LockSupport.park会响应中断interrupt,从而不再阻塞线程。
三、源码
- park
public static void park() {
U.park(false, 0L);
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker); // 设置阻塞原因。
U.park(false, 0L); // 调用底层阻塞线程
setBlocker(t, null); // 清理阻塞原因,防止调用getBlocker()获取到错误原因。注意此行代码并非立刻执行,会直到阻塞解除才执行。
}
// 带时间的阻塞。
public static void parkNanos(long nanos) {
if (nanos > 0)
U.park(false, nanos);
}
public static void parkUntil(long deadline) {
U.park(true, deadline);
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(true, deadline);
setBlocker(t, null);
}
四、补充
4.1 LockSupport.park执行后,线程状态 会变成WAITING,而synchronzed 会使线程阻塞,线程会进入 BLOCKED 状态。
4.1.1 代码
public class LockBase {
public static void main(String[] args) {
final ParkThread parkThread = new ParkThread();
parkThread.start();
sleep(5);
System.out.println(parkThread.getState()); // 线程状态为WAITING
}
private static void sleep(int secs) {
try {
TimeUnit.SECONDS.sleep(secs);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
}
static class ParkThread extends Thread{
@Override
public void run() {
System.out.println(System.currentTimeMillis() + " :ParkThread enter");
System.out.println(System.currentTimeMillis() + " :LockSupport.park()");
LockSupport.park(); // 阻塞
System.out.println(System.currentTimeMillis() + ": ParkThread run");
}
}
}
4.1.2 执行结果

分析
LockSupport.park执行后,在等待信号量可用,并非synchronzed底层的Mutex Lock。
4.2 推荐使用 LockSupport 来做线程的阻塞与唤醒(线程间协同工作),而非synchronzed 中的wait notify,因为它具备如下优点:
- 以线程为操作对象更符合阻塞线程的直观语义
- 操作更精准,可以准确地唤醒某一个线程(
notify随机唤醒一个线程,notifyAll唤醒所有等待的线程) - 无需竞争锁对象(以线程作为操作对象),不会因竞争锁对象产生死锁问题
LockSupport.unpark与LockSupport.park没有严格的执行顺序,不会因执行顺序引起永久阻塞。



1万+

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



