月泉的博客

JUC-LockSupport

月泉 并发编程JUC

LockSupport是什么

LockSupport类是一个常用的用来实现锁或使一个线程阻塞的工具类(通常用来实现这些,当然有其它用途也可以的,只是个工具类而已),它主要提供了2个方法

  • park
  • unpark

park

阻塞当前线程并阻止该线程参与调度,除非其关联了许可证

unpark

给线程关联一个许可证(令它恢复线程调度)

小结

乍一看,其实parkunpark非常类似于waitnotify,那么既然有waitnotify了那么为什么还要在写一个和它类似功能的东西出来?其实只是类似而已,使用waitnotify你必须得先获取到监视器锁才能够使用否则将会抛出IllegalMonitorStateException而使用parkunpark不需要先获得监视器锁才能用。

其底层基本都是使用Unsafe类中的方法来实现的,而Unsafe中的方法都是native方法,而naive方法是由虚拟机层面上去做的实现,所以本文不对其背后的原理机制做描述(PS:虽然违背了我一贯的习惯,但是以后可能会写一下背后的原理吧。)

LockSupport怎么用

public static void main(String[] args) {
    Thread threadA = new Thread(() -> {
        System.out.println("The thread will pack");
        LockSupport.park(); // 1
        System.out.println("The thread packed");
    });
    threadA.start();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LockSupport.unpark(threadA); //2
}

1处调用后线程将会被阻塞,等到2处调用时恢复线程调度,等待调度执行

那么再来看一个例子

public static void main(String[] args) {
    Thread threadA = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("The thread sleeped");
        System.out.println("The thread will pack");
        LockSupport.park();
        System.out.println("The thread packed");
    });
    threadA.start();
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LockSupport.unpark(threadA);
}

该例会发现,在调用完park后就立即返回了,是因为在使用unpark时已经给过该线程许可了,而park恢复调度的条件就是有一个可用的许可证所以会立即返回

park

除了无参的park以外还有几个有参的重载函数

public static void park(Object blocker);
public static void parkNanos(Object blocker, long nanos);
public static void parkUntil(Object blocker, long deadline);
public static void parkNanos(long nanos);
public static void parkUntil(long deadline);

park(Object blocker)

使用该方法会将blocker赋值给线程对象中的parkBlocker实例对象中,用于增加阻塞标识,看几个案例很直白的可以理解清楚。

LockSupport.pack()

使用该方法阻塞线程,然后我输入jstack查看线程情况

"main" #1 prio=5 os_prio=31 cpu=289.76ms elapsed=13.36s tid=0x00007ff598000000 nid=0x1a03 waiting on condition  [0x000070000129b000]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park(java.base@11.0.1/Native Method)

当我使用blocker

class Task extends Thread{
    @Override
    public void run() {
        LockSupport.park(this);
    }
}

使用jstack查看线程情况

"Thread-0" #12 prio=5 os_prio=31 cpu=0.32ms elapsed=49.76s tid=0x00007f8f658e7000 nid=0xa103 waiting on condition  [0x0000700002463000]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park(java.base@11.0.1/Native Method)
	- parking to wait for  <0x0000000787ed1ac8> (a org.yuequan.recast.thread.basic.Task)
	at java.util.concurrent.locks.LockSupport.park(java.base@11.0.1/LockSupport.java:194)

你会发现日志上回多一行parking to wait for…

接着介绍其它的API

parkNanos(Object blocker, long nanos)

阻塞对象加nanos,是指在阻塞到设定的纳秒时间后自动返回(如果期间内没有被unpark)

parkUntil(Object blocker, long deadline)

后面的单位是一个时间戳,要从1970到要阻塞的时间一起来计算来时阻塞到设定的时间后自动返回(如果期间没有被unpark)

parkNanos(long nanos)

阻塞到设定的纳秒时间后自动返回(如果期间内没有被unpark)

parkUntil(long deadline)

从1970到要阻塞的时间一起来计算来时阻塞到设定的时间后自动返回(如果期间没有被unpark)

注意事项

在使用park时如果发生中断,park也会直接返回但是不会抛出中断异常InterruptedException,所以在判断阻塞条件的时候,请把该事项考虑进去,是否需要照顾到中断。

月泉
伪文艺中二青年,热爱技术,热爱生活。