java.util.concurrent.locks.LockSupport
是 Java 并发编程中的一个非常有用的线程阻塞工具类,它包含可以阻塞和唤醒线程的方法。这个类是Java并发编程中的基础工具之一,通常用于构建锁或其他同步组件。LockSupport的所有方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。
LockSupport中的park()和unpark()方法分别用于阻塞线程和解除线程的阻塞状态。调用park()方法后,当前线程会被挂起,直到它被其他线程通过unpark()方法唤醒,或者线程被中断,或者调用该方法的线程从调用park()方法开始已经过了一个不可预知的时间。需要注意的是,LockSupport不会释放任何锁资源,因此在调用park()之前应确保当前线程没有持有任何可能导致死锁的锁。
LockSupport实际上是对线程等待/通知机制(即Object类中的wait/notify/notifyAll方法)的一种改进和扩展。与传统的等待/通知机制相比,LockSupport提供了更精确的线程阻塞和唤醒控制,以及更好的可移植性和可维护性。此外,LockSupport还可以与Java的并发锁(如ReentrantLock)和条件变量(如Condition)等高级并发工具结合使用,以实现更复杂的线程同步和协作模式。
LockSupport提供了一种挂起和恢复线程的机制,通常与线程同步原语(如锁和条件)一起使用。但是,与 Object.wait()
和 Object.notify()
或 Object.notifyAll()
相比,LockSupport
提供了一种更灵活的线程挂起和恢复方法。
LockSupport.park()
unpark
,线程被中断,或者调用该方法的线程从调用 park
方法开始已经过了一个不可预知的时间。java.util.concurrent.locks
包中的锁一起使用。LockSupport.unpark(Thread thread)
LockSupport.park()
挂起的线程才能被 unpark()
恢复。LockSupport
来挂起和恢复线程。这比使用内置的等待/通知机制更加灵活和高效。java.util.concurrent.locks
一起使用:这个包中的锁(如 ReentrantLock
)和条件(如 Condition
)通常与 LockSupport
一起使用来实现复杂的线程同步模式。LockSupport
来挂起线程,直到事件或条件满足。下面代码使用 LockSupport
来挂起和恢复线程:
import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("线程开始运行...");
// 做一些工作...
System.out.println("线程挂起...");
LockSupport.park(); // 挂起线程
if (Thread.currentThread().isInterrupted()) {
System.out.println("线程被中断...");
} else {
System.out.println("线程被恢复...");
}
// 继续执行其他任务...
});
thread.start();
// 让主线程睡眠一段时间,确保子线程已经挂起
Thread.sleep(2000);
System.out.println("尝试恢复线程...");
LockSupport.unpark(thread); // 恢复线程
// 让主线程再睡眠一段时间,确保子线程有机会执行完毕
Thread.sleep(2000);
}
}
让线程等待和唤醒是并发编程中的常见需求。以下是三种常用的方法,分别是使用Object
类的wait()
/notify()
/notifyAll()
方法、使用Lock
和Condition
接口,以及使用LockSupport
类的park()
和unpark()
方法。
Object
类的wait()
/notify()
/notifyAll()
方法这是Java中最早提供的线程等待和唤醒机制。wait()
方法会使当前线程等待,直到其他线程调用同一个对象的notify()
或notifyAll()
方法。
public class WaitNotifyExample {
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程1等待");
lock.wait(); // 线程等待
System.out.println("线程1被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程2唤醒其他线程");
lock.notify(); // 唤醒等待的线程
}
});
thread1.start();
Thread.sleep(1000); // 确保thread1先执行
thread2.start();
}
}
Lock
和Condition
接口java.util.concurrent.locks.Lock
接口和它的实现类(如ReentrantLock
)提供了比synchronized
更灵活的锁机制。Condition
接口与Lock
一起使用,可以实现等待/通知模式。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockConditionExample {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
lock.lock();
try {
System.out.println("线程1等待");
condition.await(); // 线程等待
System.out.println("线程1被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
lock.lock();
try {
System.out.println("线程2唤醒其他线程");
condition.signal(); // 唤醒等待的线程
} finally {
lock.unlock();
}
});
thread1.start();
Thread.sleep(1000); // 确保thread1先执行
thread2.start();
}
}
LockSupport
类的park()
和unpark()
方法LockSupport
它可以在线程中的任何位置阻塞线程的执行。park()
方法用于阻塞线程,unpark()
方法用于解除线程的阻塞状态。
import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.println("线程1开始运行");
LockSupport.park("等待唤醒"); // 线程等待
System.out.println("线程1被唤醒");
});
thread1.start();
Thread.sleep(1000); // 确保thread1先执行并等待
System.out.println("主线程唤醒线程1");
LockSupport.unpark(thread1); // 唤醒线程1
}
}
在使用LockSupport.park()
时,建议检查线程的中断状态,因为park()
不会响应中断,除非显式地检查并处理中断。在实际应用中,可以将park()
放在一个循环中,并在循环条件中检查中断状态。
实际上 LockSupport
类的实现非常简单,并且它的很多功能都是依赖于 JVM 的底层支持的。它的核心方法是 park
和 unpark
,分别用于阻塞和解除阻塞线程。
下面是 LockSupport
类的一些关键部分的源码分析:
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
// Blocks until unparked, interrupted, or the caller spuriously returns.
public static void park() {
Unsafe.getUnsafe().park(false, 0L);
}
// Blocks until unparked, interrupted, the caller spuriously returns,
// or the deadline passes, whichever comes first.
public static void parkNanos(long nanos) {
if (nanos > 0)
Unsafe.getUnsafe().park(false, nanos);
}
// Blocks until unparked, interrupted, the caller spuriously returns,
// or approximately deadline milliseconds have passed, whichever comes first.
public static void parkUntil(long deadline) {
Unsafe.getUnsafe().park(true, deadline);
}
// Unblocks the thread blocked by park().
public static void unpark(Thread thread) {
if (thread != null)
Unsafe.getUnsafe().unpark(thread);
}
// Hotspot implementation of park/unpark may need some help to find
// threads blocked on the LockSupport objects created by this class.
// These methods are natively implemented in the JDK.
private static native void setBlocker(Thread t, Object arg);
private static native Object getBlocker(Thread t);
// Disables the current thread for thread scheduling purposes, for up to the
// specified waiting time, unless the `permit` is available.
// This method is designed to be used as a tool for creating higher-level
// synchronization utilities, and is not intended for general use.
// It supports a single permit, which is initially unavailable, and
// which becomes available when `unpark` is invoked, or if the caller
// spuriously returns.
// ... additional methods related to permit (not shown here)
}
从上面的源码片段中,我们可以看到以下几点:
LockSupport
是一个工具类,不能被实例化(构造函数是私有的)。
park
方法和一个 unpark
方法。这些方法都是静态的,可以直接通过类名调用。
park
方法用于阻塞当前线程,直到它被 unpark
,线程被中断,或者出现其他未指定的情况(即“spurious wakeup”,虽然在现代 JVM 中这种情况很少见)。
parkNanos
和 parkUntil
方法允许设置一个时间限制,在此时间之后线程会自动解除阻塞,即使没有被其他线程显式地 unpark
。
unpark
方法用于解除指定线程的阻塞状态。如果线程没有被阻塞,或者由于某种原因已经解除阻塞,调用 unpark
不会有任何效果。
Unsafe
类来实现的,这是一个提供低级别、非安全、操作系统级别访问的类。Unsafe
类中的 park
和 unpark
方法是本地方法(通过 JNI 调用),它们直接与 JVM 的线程调度器交互。
setBlocker
和 getBlocker
方法是本地方法,用于在 JVM 内部跟踪由 LockSupport
阻塞的线程。这些方法通常不用于应用程序代码。
LockSupport.park()
挂起线程时,应该确保有一个明确的机制来恢复(通过 unpark()
)或中断线程,以避免线程永久挂起。LockSupport
不会释放任何锁资源,因此在调用 park()
之前应该确保线程没有持有任何可能导致死锁的锁。Object.wait()
和 Thread.sleep()
不同,LockSupport.park()
不会响应中断,除非你在调用 park()
之前或之后显式检查中断状态。但是,如果线程在 park()
期间被中断,那么从 park()
返回后,中断状态将被清除(设置为 false
),除非你在此之前调用了 Thread.interrupted()
来检查并清除中断状态。因此,通常建议在调用 park()
的循环中检查中断状态。LockSupport
的一些常见面试题LockSupport.park()
和 Thread.sleep()
有什么区别?答案:
LockSupport.park()
:它会暂停当前线程的执行,直到它被 unpark
,线程被中断,或者出现虚假唤醒(spurious wakeup,但在现代JVM中很少见)。park
不需要处理 InterruptedException
,并且它不会保持任何锁。Thread.sleep()
:它会使当前线程休眠指定的时间,时间到了之后线程会变为就绪状态。sleep
期间线程不会释放任何锁,而且必须处理 InterruptedException
。LockSupport
的主要用途是什么?答案:
LockSupport
主要用于创建锁和其他同步工具的基础类。它提供了一种机制,允许线程等待某个条件成立,而不需要轮询。这种等待是通过 park
和 unpark
方法来实现的,它们是构建高效锁和其他同步工具的关键。
LockSupport.park()
为什么比传统的线程等待方式更高效?答案:
传统的线程等待方式通常涉及到轮询(polling)或者使用 Thread.sleep()
,这些方法都会浪费CPU资源,因为它们要么不断地检查条件,要么使线程进入睡眠状态,而在条件可能变为真时不会立即唤醒。LockSupport.park()
提供了一种更有效的方式,它允许线程在条件不满足时进入无消耗等待状态,直到它被 unpark
或中断,这样可以减少CPU的占用和上下文切换的开销。
LockSupport
是如何工作的?答案:
LockSupport
中的 park
和 unpark
方法是通过底层的 Unsafe
类来实现的,这是一个提供低级别、非安全、操作系统级别访问的类。这些方法直接与JVM的线程调度器交互,将线程置于一种特殊的等待状态,在这种状态下线程不会消耗CPU资源,直到它被 unpark
或中断。
LockSupport
时需要注意什么?答案:
使用 LockSupport
时需要注意以下几点:
park
方法可能会导致线程进入无限期等待,因此需要确保有相应的机制(如中断或 unpark
)来唤醒线程。LockSupport
本身不提供锁或其他同步机制,它通常与其他同步原语(如 ReentrantLock
)一起使用。LockSupport
是基于底层的 Unsafe
类实现的,因此在使用时需要谨慎,避免在不适当的上下文中使用它。InterruptedException
非常重要,尤其是在使用 LockSupport
时。即使 park
方法本身不会抛出 InterruptedException
,但在使用它构建的同步工具中可能需要处理中断。LockSupport 提供了 park 和 unpark 方法,用于阻塞和解除阻塞线程,是构建锁和其他同步工具的基础。与传统的 Thread.sleep() 或Thread.yield() 不同,LockSupport 不需要线程处理 InterruptedException,而且它不会保持任何锁,因此在构建高效、响应式的并发系统时特别有用。
LockSupport.park() 方法允许线程等待某个条件成立,而不会消耗CPU资源,直到它被其他线程 unpark 或被中断。这种等待方式比轮询或使用 sleep() 方法更高效,因为它减少了CPU的占用和上下文切换的开销。
但lockSupport 本身不提供锁或其他同步机制,它通常与其他同步原语(如 ReentrantLock)一起使用,以实现更复杂的同步需求。此外,在使用 LockSupport 时,需要确保有相应的机制来唤醒等待的线程,避免线程进入无限期等待。