【JUC】 LockSupport类的park等待和unpark唤醒

【JUC】 LockSupport类的park等待和unpark唤醒

1. 概述

LockSuppot:用于创建锁和其他同步类的基本线程阻塞原语。

这个类与每个使用它的线程相关联,一个许可证(在Semaphore类的意义上)。 如果许可证可用,则呼叫parkpark返回,在此过程中消耗它; 否则可能会阻止。 致电unpark使许可证可用,如果尚不可用。 (与信号量不同,许可证不能累积,最多只有一个。)

LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能, 每个线程都有一个许可(permit),permit只有两个值1和零,默认是零。可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。


2. API解释

  • static void park() :禁止当前线程进行线程调度(就是阻塞),除非许可证可用。

    permit默认是零,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为零并返回。

    public static void park() {
          
          
        UNSAFE.park(false, 0L);
    }
    
  • static void unpark(Thread thread):为指定的线程提供许可证(前提是尚未提供过许可证)。

    调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park()方法会立即返回。

    public static void unpark(Thread thread) {
          
          
        if (thread != null)
            UNSAFE.unpark(thread);
    }
    

3. 代码示例

3.1 先等待再唤醒

先等待再唤醒,这是一般情况。

public static void main(String[] args) {
    
    
    Thread t1 = new Thread(() -> {
    
    
        System.out.println(Thread.currentThread().getName() + "\t ----come in" + System.currentTimeMillis());
        LockSupport.park();
        System.out.println(Thread.currentThread().getName() + "\t ----被唤醒" + System.currentTimeMillis());
    }, "t1");
    t1.start();

    //暂停几秒钟线程
    try {
    
     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
    
     e.printStackTrace(); }

    new Thread(() -> {
    
    
        LockSupport.unpark(t1);
        System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
    }, "t2").start();

}

运行结果如下:

image-20230406000943421


3.2 先唤醒再等待

先唤醒再等待,这是特殊情况。

public static void main(String[] args) {
    
    
    Thread t1 = new Thread(() -> {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t ----come in" + System.currentTimeMillis());
        LockSupport.park();
        System.out.println(Thread.currentThread().getName() + "\t ----被唤醒" + System.currentTimeMillis());
    }, "t1");
    t1.start();

    new Thread(() -> {
    
    
        LockSupport.unpark(t1);
        System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
    }, "t2").start();

}

运行结果如下:

image-20230406001202624

注:park和unpark必须成对出现。


4. 区别

除了LockSupport类的park、unpark方法可以实现等待唤醒功能,Object类的wait、notify以及Condition接口的await、signal也同样能实现。

不过LockSupport是它们俩的升级版本。

Object类和Condition接口的方法在使用上都有一定的限制条件。


4.1 wait和notify的限制

  1. wait和notify方法必须要在同步代码块中,且成对出现使用。
  2. 必须要先wait才能notify,否则会一直阻塞。

4.2 await和signal的限制

  1. Condition中的await和signal方法必须先获取锁。
  2. 一定要先await后signal,否则会一直阻塞。

而LockSupport类的方法就没有以上限制。

猜你喜欢

转载自blog.csdn.net/Decade_Faiz/article/details/129980289