文章目录
- 一、概述
- 二、使用方法
- 三、测试示例1
- 四、测试示例2
一、概述
-
LockSupport 是Java并发包中的一个工具类,用于线程的阻塞和唤醒。它提供了一种基于线程的许可(permit)的方式来实现线程的阻塞和唤醒,而不需要显式地使用锁。例如某个条件满足后阻塞线程,然后等待某个条件满足后再继续执行、实现线程间的协作等。
-
LockSupport 的主要方法包括:
- park(): 阻塞当前线程,使其进入waiting状态,直到被其他线程调用unpark(Thread thread)方法唤醒或被中断。
- park(Object blocker):类似于park()方法,但可以关联一个blocker对象,用于监视线程的阻塞情况,方便调试和分析。
- parkNanos(long nanos): 阻塞当前线程,最长不超过指定的纳秒数。在指定的时间内,线程可能被其他线程调用unpark(Thread thread)方法唤醒或被中断。
- parkUntil(long deadline): 阻塞当前线程,直到指定的时间戳(以毫秒为单位)。在指定的时间内,线程可能被其他线程调用unpark(Thread thread)方法唤醒或被中断。
- unpark(Thread thread): 唤醒指定线程,使其从waiting状态返回正常执行。如果之前没有调用过park()方法,调用unpark()方法也能确保之后调用park()方法时不会阻塞线程。
-
LockSupport 的使用相对简单,可以在任何地方使用,而不仅限于同步代码块或同步方法中。它通常与其他同步机制结合使用,用于实现线程的阻塞和唤醒。
二、使用方法
-
使用 LockSupport 的方法如下:
- 在需要等待的地主调用 LockSupport.park() ,使用线程处理等待状态。
- 当条件满足后使用 LockSupport.unpark(thread) 唤醒线程,使用线程继续执行。
import java.util.concurrent.locks.LockSupport;public class LockSupportExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {// 执行业务逻辑LockSupport.park(); // 阻塞当前线程// 继续执行业务逻辑});thread1.start();try {Thread.sleep(2000); // 等待2秒钟} catch (InterruptedException e) {e.printStackTrace();}LockSupport.unpark(thread1); // 唤醒 thread1 线程} }
三、测试示例1
-
在下面示例中,创建了一个新的线程并启动它。在新线程中,第一行输出语句执行后,调用了 LockSupport.park() 方法,导致当前线程阻塞。在主线程中,等待2秒后,调用了 LockSupport.unpark(thread) 方法,唤醒了被阻塞的线程。被唤醒的线程将继续执行并输出"线程-继续执行业务逻辑"。
注意:LockSupport 没有内部状态(如等待队列),它只是给线程提供了阻塞和唤醒的能力。因此,
unpark() 方法可以在 park() 方法之前调用
,而不会导致线程永久阻塞。每个线程都有一个许可(permit),park() 方法会消耗掉一个许可,而 unpark() 方法会补充一个许可。如果线程在调用 park() 方法之前已经有了许可,那么调用 park() 方法时不会阻塞。这使得LockSupport
具有更灵活的使用方式,可以用于实现更复杂的线程同步和控制逻辑。(还有许可最多也只能颁发一个)package top.yiqifu.study.p004_thread;import java.util.concurrent.locks.LockSupport;public class Test096_LockSupport {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("线程-执行业务逻辑");System.out.println("线程-进行等待状态");LockSupport.park(); // 阻塞当前线程System.out.println("线程-被唤醒");System.out.println("线程-继续执行业务逻辑");});thread1.start();try {Thread.sleep(2000); // 等待2秒钟} catch (InterruptedException e) {e.printStackTrace();}System.out.println("开始唤醒线程");LockSupport.unpark(thread1); // 唤醒 thread1 线程}}
四、测试示例2
-
在下面示例中,借助 ConcurrentLinkedQueue队列,实一个单机版本的发布订阅。
package top.yiqifu.study.p004_thread;import java.io.IOException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.locks.LockSupport;public class Test097_LockSupportPS {private static volatile boolean isFinish = false;private static final ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();private static Thread mainThread = Thread.currentThread();private static Thread readerThread, writerThread;public static void main(String[] args) throws IOException {readerThread = new Thread(Test097_LockSupportPS::subscribe);writerThread = new Thread(Test097_LockSupportPS::publish);readerThread.start();writerThread.start();LockSupport.park();System.out.println("测试结束");}private static void subscribe() {while (!isFinish) {if (queue.isEmpty()) {// 队列为空,订阅线程进入等待状态LockSupport.park();// 线程被唤醒}String message = queue.poll();if (message != null) {System.out.println("收到订阅消息: " + message);}}System.out.println("退出订阅");LockSupport.unpark(mainThread);}private static void publish() {for (int i = 1; i <= 10; i++) {String message = "Message " + i;queue.offer(message);System.out.println("发布消息: " + message);// 唤醒读线程LockSupport.unpark(readerThread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}isFinish = true;LockSupport.unpark(readerThread);}}