LockSupport工具类

LockSupport工具类主要用来挂起和唤醒线程。它会与每个使用它的线程关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。底层实现也是使用的Unsafe类,而且使用的是本地方法。介绍一下几个函数:

1.park()和unpark()方法

void park()方法

若调用park方法的线程已经拿到了与LockSupprot关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程调度,即被阻塞挂起。如下代码:

    public static void main(String[] args) throws Exception {
    
        System.out.println("begin park!");
        LockSupport.park();
        System.out.println("end park!");
    }

结果只会输出"begin park!",因为默认情况下调用线程不持有许可证。

在其他线程如OtherThread调用LockSupport.unpark(Thread thread)方法时,若thread因为调用park()方法被阻塞,则此时会返回;若其他线程调用了阻塞线程的interrupt()方法,即在OtherThread调用了thread.interrupt()方法,设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回,即阻塞的线程是响应中断的。

void unpark(Thread thread)方法

当一个线程OtherThread调用LockSupport.unpark(thread)时,若参数thread没有持有thread与LockSupport的许可证,则会让thread线程持有;若thread之前因调用park()而被挂起,则调用unpark(thread)后,该线程会被唤醒;若thread之前没调用过thread.park()方法,则调用unpark(thread)方法后,再调用thread.park()方法,会立即返回。看一个例子:

    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("child thread begin park!");

                LockSupport.park();

                System.out.println("child thread unpark!");
            }
        });

        thread.start();

        Thread.sleep(1000);

        System.out.println("main thread begin unpark!");

        LockSupport.unpark(thread);
    }

运行结果如下。首先thread使用park方法将自己挂起,然后主线程输出语句后调用LockSupport.unpark(thread)方法让子线程持有许可证,子线程调用的park()方法就返回了。

child thread begin park!
main thread begin unpark!
child thread unpark!
总结

其实LockSupport方法中的park()和unpark()方法用来阻塞或恢复线程,其实就是通过一个抽象的概念即:当前线程是否有许可证

  • 若线程第一次调用park()方法,默认没有许可证,所以会被挂起。等待其他线程调用unpark(thread)将被挂起的线程做为参数时,才能返回。
  • 若线程没有调用过park()方法,则调用了unpark(thread)方法后,已经把许可证给了thread了,所以此后若thread再调用park()方法,会立即返回
  • 要注意的是,因调用park()方法而被阻塞的线程被其他线程中断而返回时并不会抛出InterruptedException异常,而且会立即返回。

2.park()方法的变形

void parkNanos(long nanos)方法

和park方法类似,只是如果线程在nanos时间内还未拿到许可证,则会自动返回,而park()则必须等到unpark()的执行才能返回。

park(Object blocker)方法

若线程在没有许可证的情况下调用park()方法而被阻塞挂起,这个blocker对象会被记录到该线程内部。这个方法还是挺有用的,一些诊断工具可以观察线程被阻塞的原因,而诊断工具往往就是通过调用getBlocker(Thread)方法来获取blocker对象从而判断是在哪一个类中被阻塞了。通过使用park(this)方法将当前对象作为参数传进去,就可以获得那个类。代码如下:

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        //设置该线程的blocker变量
        setBlocker(t, blocker);
        //挂起线程,返回时就会从这个方法返回,进而在下一步清除blocker变量
        UNSAFE.park(false, 0L);
        //线程被激活后清除blocker变量,因为一般都是在线程阻塞时才分析原因
        setBlocker(t, null);
    }

Thread类中有个变量volatile Object parkBlocker,用来存放park方法传递的blocker对象,也就是把blocker变量存放到了调用park方法的线程的成员变量里面

void parkNanos(Object blocker, long nanos)方法

相对于park(Object blocker)方法多了个超时时间

void parkUtil(Object blocker, long deadline)方法

它的代码如下:

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

此方法和parkNanos(Object blocker, long nanos)的区别是参数为nanos表示从当前算等待nanos秒时间后返回,而参数为deadline表示的是指定的一个时间点,该时间是从1970年到现在某一个时间点的毫秒值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值