LockSupport基本介绍
LockSupport
是 Java 中的一个实用工具类,它属于 java.util.concurrent.locks
包。这个类提供了基本的线程阻塞原语,这些原语用于构建同步器和锁。LockSupport
可以用于线程之间的协调,特别是当你需要阻塞一个线程直到另一个线程执行某个操作时。
LockSupport 特点
以下是 LockSupport
的一些关键点:
- 线程阻塞与唤醒:
LockSupport
提供了park
和unpark
方法来阻塞和唤醒线程。当调用park
方法时,当前线程会阻塞,直到另一个线程调用相同线程对象的unpark
方法。 park方法表示消耗一个许可,调用park方法时,如果许可可用则park方法返回,如果没有许可则一直阻塞直到许可可用。unpark方法表示增加一个许可,多次调用并不会积累许可,因为许可数最大值为1。 - 无锁编程:
LockSupport
可以用于实现无锁的编程模式,特别是在构建高性能的并发应用程序时。 - 公平性:
LockSupport
不保证公平性,也就是说,没有特定的顺序来唤醒等待的线程。如果需要公平性,可能需要使用其他同步机制。 - 简单性:
LockSupport
提供了一种简单的方法来阻塞和唤醒线程,不需要创建复杂的锁对象。 - 使用场景:它通常用于实现同步器,如信号量、屏障、倒计时锁等
LockSupport
是一个工具类,提供了基本的线程阻塞和唤醒功能,它是创建锁和其他同步组件的基础工具,内部是使用sun.misc.Unsafe类实现的。
LockSupport 例子加强理解
public class LockSupportExample {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("Thread is running");
LockSupport.park(); // 线程将会阻塞在这里
System.out.println("Thread continues after being unparked");
});
t.start();
try {
Thread.sleep(1000); // 等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(t); // 唤醒上面创建的线程
}
}
LockSupport 顺序打印ABC
public class LockSupporTest {
public static void main(String[] args) {
// 创建三个线程,分别用于打印字符 'A'、'B' 和 'C'
Thread threadC = new Thread(() -> printC());
Thread threadB = new Thread(() -> printB(threadC)); // threadB 需要 threadC 作为参数
Thread threadA = new Thread(() -> printA(threadB)); // threadA 需要 threadB 作为参数
// 启动三个线程
threadA.start();
threadB.start();
threadC.start();
// 主线程进入一个无限循环,这里可能需要一个条件来打破循环,否则程序将无法退出
while (true) {
// 这里应该添加一个 break 条件,或者使用其他方式来结束程序,否则程序会一直运行
}
}
// printA 方法,接收一个线程对象作为参数
private static void printA(Thread thread) {
// 让当前线程休眠 5000 毫秒
ThreadUtil.sleep(5000);
System.out.println("a");
// 唤醒传入的线程对象(在本例中是 threadB)
LockSupport.unpark(thread);
}
// printB 方法,同样接收一个线程对象作为参数
private static void printB(Thread thread) {
// 让当前线程休眠 2000 毫秒
ThreadUtil.sleep(2000);
// 当前线程阻塞,直到另一个线程调用它的 LockSupport.unpark()
LockSupport.park();
System.out.println("b");
// 唤醒传入的线程对象(在本例中是 threadC)
LockSupport.unpark(thread);
}
// printC 方法,没有接收参数
private static void printC() {
// 让当前线程休眠 1000 毫秒
ThreadUtil.sleep(1000);
// 当前线程阻塞,直到另一个线程调用它的 LockSupport.unpark()
LockSupport.park();
// 打印字符 'C'
System.out.println("c");
}
}
LockSupport面试题
关于 LockSupport
的一些常见面试题及其答案如下:
-
LockSupport.park()
和Thread.sleep()
有什么区别?LockSupport.park()
:它会暂停当前线程的执行,直到它被unpark
,线程被中断,或者出现虚假唤醒(spurious wakeup,但在现代JVM中很少见)。park
不需要处理InterruptedException
,并且它不会保持任何锁。Thread.sleep()
:它会使当前线程休眠指定的时间,时间到了之后线程会变为就绪状态。sleep
期间线程不会释放任何锁,而且必须处理InterruptedException
87。
-
LockSupport
的主要用途是什么?LockSupport
主要用于创建锁和其他同步工具的基础类。它提供了一种机制,允许线程等待某个条件成立,而不需要轮询。这种等待是通过park
和unpark
方法来实现的,它们是构建高效锁和其他同步工具的关键87。
-
LockSupport.park()
为什么比传统的线程等待方式更高效?- 传统的线程等待方式通常涉及到轮询(polling)或者使用
Thread.sleep()
,这些方法都会浪费CPU资源,因为它们要么不断地检查条件,要么使线程进入睡眠状态,而在条件可能变为真时不会立即唤醒。LockSupport.park()
提供了一种更有效的方式,它允许线程在条件不满足时进入无消耗等待状态,直到它被unpark
或中断,这样可以减少CPU的占用和上下文切换的开销87。
- 传统的线程等待方式通常涉及到轮询(polling)或者使用
-
LockSupport
是如何工作的?LockSupport
中的park
和unpark
方法是通过底层的Unsafe
类来实现的,这是一个提供低级别、非安全、操作系统级别访问的类。这些方法直接与JVM的线程调度器交互,将线程置于一种特殊的等待状态,在这种状态下线程不会消耗CPU资源,直到它被unpark
或中断87。
-
使用
LockSupport
时需要注意什么?-
使用
LockSupport
时需要注意以下几点:park
方法可能会导致线程进入无限期等待,因此需要确保有相应的机制(如中断或unpark
)来唤醒线程。LockSupport
本身不提供锁或其他同步机制,它通常与其他同步原语(如ReentrantLock
)一起使用。- 由于
LockSupport
是基于底层的Unsafe
类实现的,因此在使用时需要谨慎,避免在不适当的上下文中使用它。 - 在多线程编程中,正确地处理中断和
InterruptedException
非常重要,尤其是在使用LockSupport
时。即使park
方法本身不会抛出InterruptedException
,但在使用它构建的同步工具中可能需要处理中断。
-
-
LockSupport.unpark
能否唤醒一个没有被park
的线程?- 不能。
LockSupport.unpark
只对已经通过LockSupport.park
阻塞的线程有效。如果一个线程没有被park
,unpark
对其没有效果。
- 不能。
-
LockSupport.park
是否会响应中断?- 是的。如果一个线程在
park
状态时被中断,park
方法会立即返回,并且线程的中断状态将被清除。因此,通常建议在park
之后检查中断状态。
- 是的。如果一个线程在
-
LockSupport
是否保证公平性?- 不保证。
LockSupport
不保证按照unpark
调用的顺序唤醒线程,它的唤醒操作是不可预测的。
- 不保证。
-
LockSupport.park
和Object.wait
有何不同?LockSupport.park
不需要获取对象的锁就能阻塞线程,而Object.wait
必须在同步代码块或方法中调用,并且需要线程持有相应的监视器锁。此外,park
不会抛出InterruptedException
,而wait
会。
-
LockSupport
是否可以与其他锁机制一起使用?- 是的。
LockSupport
可以与java.util.concurrent.locks
包中的锁如ReentrantLock
结合使用,以及可以用于实现自定义同步器和锁。
- 是的。
-
如何使用
LockSupport
实现线程间的协调?- 可以通过一个共享的协调对象或者信号量来实现。一个线程可以
park
直到协调对象的状态满足某个条件,而另一个线程在条件满足时unpark
等待的线程。
- 可以通过一个共享的协调对象或者信号量来实现。一个线程可以
-
LockSupport
在 Java 并发包中扮演什么角色?LockSupport
是 Java 并发包中的一个基础工具类,它提供了线程阻塞和唤醒的基本操作。它在实现更高级的并发组件如Semaphore
、CountDownLatch
以及其他自定义同步器时非常有用。
-
解释
LockSupport
中的许可(permit)的概念。LockSupport
使用一个内部的许可(permit)机制来控制线程的阻塞和唤醒。每个线程有一个与之关联的许可状态,初始值为0。当线程调用park
时,如果它的许可状态为0,它将阻塞;如果许可状态为1,它将消费这个许可,将其状态更新为0,并继续执行。unpark
方法会增加目标线程的许可状态到1,如果线程因为许可状态为0而阻塞,这将唤醒它。
-
LockSupport.park
是否有可能发生伪唤醒(spurious wakeup)?- 是的,尽管在现代 JVM 中这种情况很少见,但理论上
LockSupport.park
可能会发生伪唤醒,即线程在没有调用unpark
的情况下被唤醒。因此,使用park
方法时通常会在一个循环中,以确保线程在条件不满足时能够重新阻塞。
- 是的,尽管在现代 JVM 中这种情况很少见,但理论上