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
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。