Java中的线程休眠大法系列(三)LockSupport.park()


前言

Java的线程休眠我们从Thread.sleep到Object.wait(),都进行了逐一讲解,现在我们研究最后一种方式LockSupport.park()。


一、看看JDK的代码注释

	/**
     * Disables the current thread for thread scheduling purposes unless the
     * permit is available.
     翻译:除非许可证可用,否则禁用当前出于线程调度目的线程。
     * <p>If the permit is available then it is consumed and the call
     * returns immediately; otherwise the current thread becomes disabled
     * for thread scheduling purposes and lies dormant until one of three
     * things happens:
     翻译:如果许可是可用的,那么这个许可会被消费掉,并且方法立刻返回。
     否则的话,当前的线程会被变成不可以用,并处于休眠状态,直至下面三种情况发生:
     * <ul>
     *
     * <li>Some other thread invokes {@link #unpark unpark} with the
     * current thread as the target; or
     翻译:其他线程调用了unpark(Thread t) 方法,参数t就是被休眠park的线程
     
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     *
     翻译:其他线程interrupts了此线程
     
     * <li>The call spuriously (that is, for no reason) returns.
     * </ul>
     *
     翻译:调用方法莫名其妙的返回了(没有什么原因)
     
     * <p>This method does <em>not</em> report which of these caused the
     * method to return. Callers should re-check the conditions which caused
     * the thread to park in the first place. Callers may also determine,
     * for example, the interrupt status of the thread upon return.
     * 
     * 翻译:该方法不会报道是由于哪一种情况导致的返回。
     * 调用者应该自行检查原因,调用者也可能根据返回的线程状态,进行推断。
     * 
     */
    public static void park() {
        UNSAFE.park(false, 0L);
    }

从上面的注释我们可以看出来和之前的Thread.sleep、Object.wait()不同的是,该方法不会直接进入休眠,而是会先根据一个permit来进行判断,如果有了permit,那么方法直接返回,如果没有得到permit才会进入休眠状态。
那么进入休眠状态后,有三种情况会被打破
1、其他线程调用了unpark()方法,将该线程unpark了。
2、其他线程interrupts了该线程
3、其他莫名其妙的原因导致的
最后,就是方法不会告知调用方具体是哪一种原因导致了解除睡眠状态,调用者应该重新检查线程,在第一次被park的条件。调用者也可以根据返回,来判断线程的中断状态。

另外,我们也可以看出来,此方法不会抛出受检查异常,这一点和Thread.sleep、Object.wait()不同。

二、案例

1. 查看线程状态

代码如下:

package com.kinyang.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
 * @author KinYang.Liu
 * @date 2021/6/15 9:46 上午
 */
public class LockSupportTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            LockSupport.parkNanos(3000*4000*1000L);
        });
        Thread t2 = new Thread(()->{
            LockSupport.park();
        });
        t1.start();
        t2.start();
        Thread.sleep(1000L);
        System.out.println("查看一下 t1 线程执行parkNanos的状态:"+t1.getState());
        System.out.println("查看一下 t2 线程执行park的状态:"+t2.getState());
    }
}

运行结果
在这里插入图片描述
从结果我们可以看出来:LockSupport.park()方法会进入WAITING状态,而LockSupport.parkNanos(long nanos) 会进入TIMED_WAITING状态,这一点和Object.wait是一致的。
可以指定一个休眠时间,不指定就是永久休眠,不会自动唤醒。

2.线程被打断,不抛异常

代码如下:

package com.kinyang.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
 * @author KinYang.Liu
 * @date 2021/6/15 9:46 上午
 */
public class LockSupportTest2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1 线程开始执行");
            System.out.println("t1 线程 park()  ");
            LockSupport.park();
            System.out.println("t1 线程 继续执行");
            System.out.println("t1 线程 当前的 Interrupt 状态:"+Thread.currentThread().isInterrupted());
        });

        t1.start();
        Thread.sleep(1000L);
        //  unpark t1线程
        LockSupport.unpark(t1);
    }
}

执行结果
在这里插入图片描述
当通过LockSupport.unpark()唤醒线程的时候,线程的Interrupt 状态是fasle

package com.kinyang.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
 * @author KinYang.Liu
 * @date 2021/6/15 9:46 上午
 */
public class LockSupportTest2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1 线程开始执行");
            System.out.println("t1 线程 park()  ");
            LockSupport.park();
            System.out.println("t1 线程 继续执行");
            System.out.println("t1 线程 当前的 Interrupt 状态:"+Thread.currentThread().isInterrupted());
        });

        t1.start();
        Thread.sleep(1000L);
        /// 将t1 线程打断
          t1.interrupt();
    }
}

运行结果
在这里插入图片描述
当我们通过 interrupt打断线程的时候,线程的Interrupt 状态是true。

通过上面两个例子,我们可以看到LockSupport.park();不会抛出受检查异常,当出现被打断的情况下,也不会抛出异常,但是会将Interrupt的状态修改,也就是当我们的线程被唤醒后,我们可以通过Interrupt的状态来判断,我们的线程是不是被interrupt的还是被unpark或者到达指定休眠时间。
这一点和Thread.sleep和Object.wait()不同,他们两个当线程被打断的时候,会抛出异常,在catch到异常的时候,interrupt的状态其实是没有的,也就是你在catch代码块中得到的interrupt状态永远是false。

2.是否会释放锁

代码如下:

package com.kinyang.lockSupport;

import java.util.Scanner;
import java.util.concurrent.locks.LockSupport;

/**
 * @author KinYang.Liu
 * @date 2021/6/15 9:46 上午
 */
public class LockSupportTest3 {
    private static Object monitor = new Object();
    public static void main(String[] args) throws InterruptedException {
         创建一个 t1 线程
        Thread t1 = new Thread(() -> {
            System.out.println("t1 尝试获取锁");
            synchronized (monitor){
                System.out.println("t1 获取到锁");
                System.out.println("t1 执行 LockSupport.parkNanos() 方法,休眠该线程!");
                /// 我们执行 parkNanos 操作,让线程进入休眠,看看其他线程是否能抢到 monitor 锁,
                ///  如果其他线程可以获取到 monitor 锁后,那么说明sleep会释放monitor锁
                ///  否则,说明parkNanos不会释放monitor锁
                LockSupport.parkNanos(1000*1000*1000*5L);
                System.out.println("t1 线程执行完成");
            }
        },"t1-thread");
         启动t1线程
        t1.start();
        /// 这里让主线程休眠1s后,确保t1线程已经执行,并获取到锁
        Thread.sleep(1000L);
        Thread t2 = new Thread(() -> {
            System.out.println("t2 尝试获取锁");
            synchronized (monitor){
                System.out.println("t2 获取到锁");
                System.out.println("执行 t2 方法");
            }
        },"t2-thread");
        t2.start();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            if (scanner.nextLine().equalsIgnoreCase("bye")){
                System.out.println("再见");
                break;
            }else {
                System.out.println("指令错误");
            }

        }
    }
}

运行结果
在这里插入图片描述

从执行结果我们就能看出来是,LockSupport.parkNanos()是不会释放锁的。
其实也很容易理解,LockSupport.park跟synchronized就没有关系,休眠线程的原理和Object.wait不同,LockSupport.park休眠线程不需要依赖monitor锁,所以也就不会涉及到释放与不释放锁,这一点和Thread.sleep()是一致的。

3.传递自定义数据

代码如下:

package com.kinyang.lockSupport;

import java.util.HashMap;
import java.util.concurrent.locks.LockSupport;

/**
 * @author KinYang.Liu
 * @date 2021/6/15 9:46 上午
 */
public class LockSupportTest4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("t1 线程开始执行");
            System.out.println("t1 线程 parkNanos()  ");
            HashMap blocker = new HashMap();
            blocker.put("reason","测试LockSupport的park的blocker");
            blocker.put("code","1000");
            LockSupport.parkNanos(blocker,2000*1000*2000L);
            System.out.println("t1 线程 继续执行");
            System.out.println("t1 线程 当前的 Interrupt 状态:"+Thread.currentThread().isInterrupted());
        });
        t1.start();
        Thread.sleep(1000L);
        Object blocker = LockSupport.getBlocker(t1);
        System.out.println("t1 线程的blocker:"+blocker);
        Thread.sleep(1000*100L);
    }
}

执行结果
在这里插入图片描述
LockSupport.park(Object blocker); 方法可以在park线程的时候,传一个Object参数。调用者在其他线程可以通过LockSupport.getBlocker(t1)参数。


总结

LockSupport.park()休眠线程,LockSupport.unpark()唤醒线程,两个方法配合使用。也可以通过LockSupport.parkNanos()指定休眠时间后,自动唤醒。
LockSupport.park()不会释放monitor锁。
线程被打断,LockSupport.park()不会抛出异常,也不会吞噬掉interrupt的状态,调用者可以获取interrupt状态,自行进行判断,线程是由于什么原因被唤醒了。
LockSupport.park()会是线程进入WAITING状态,而LockSupport.parkNanos(long nanos) 会进入TIMED_WAITING状态。
LockSupport.park(Object blocker)和LockSupport.getBlocker(t1)配合使用,可以进行自定义数据传输。

  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jianyang.liu

从来没收到过一分钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值