Java 多线程之 LockSupport (阻塞和唤醒线程)
2023-12-13 22:40:16
一、概述
-
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); } }
文章来源:https://blog.csdn.net/qifu123/article/details/134860925
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!