Java并发编程——LockSupport工具类

PunkLu 2020年01月14日 166次浏览
LockSopport工具类
# LockSupport工具类

介绍

JDK中的rt.jar包里面的LockSupport是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。

LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用Unsafe类实现的。

源码解析

1、void park()方法

如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。

如下代码直接在main函数里面调用park方法,最终只会输出begin park!,然后当前线程被挂起,这是因为在默认情况下调用线程是不持有许可证的。

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

在其他线程调用unpark(Thread thread)方法并且将当前线程作为参数时,调用park方法而被阻塞的线程会返回。另外,如果其他线程调用了阻塞线程的interrupt()方法,设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回。所以在调用park方法时最好也使用循环条件判断方式。

需要注意的是,因调用park()方法而被阻塞的线程被其他线程中断而返回时并不会抛出InterruptedException异常。

2、void unpark(Thread thread)方法

当一个线程调用unpark时,如果参数thread线程没有持有thread与LockSupport类关联的许可证,则让thread线程持有。 如果thread之前因调用park()而被挂起,则调用unpark后,该线程会被唤醒。如果thread之前没有调用park,则调用unpark方法后,再调用park方法,其会立即返回。修改代码如下:

public static void main(String[] args){
	System.out.println("begin park!");
	
	// 使当前线程获取到许可证
	LockSupport.unpark(Thread.currentThread());
	
	// 再次调用park方法
	LockSupport.park();
	
	System.out.println("end park!");
}

该代码会输出:

begin park!
end park!

再看一次例子:

public class ParkAndUnpark {

    public static void main(String[] args) throws InterruptedException{

        Thread thread = new Thread(new Runnable() {
            public void run() {
                System.out.println("child thread begin park!");

                // 调用park方法,挂起自己
                LockSupport.park();

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

        // 启动子线程
        thread.start();

        // 主线程休眠1s
        Thread.sleep(1000);

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

        // 调用unpark方法让thread线程持有许可证,然后park方法返回
        LockSupport.unpark(thread);
    }
}

运行结果:

child thread begin park!
main thread begin unpark!
child thread unpark!

3、void parkNanos(long nanos)方法

和park方法类似,如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.parkNanos(long nanos)方法后会马上返回。该方法的不同在于,如果没有拿到许可证,则调用线程会被挂起nanos时间后修改为自动返回。

另外park方法还支持带有blocker参数的方法void park(Object blocker)方法,当线程在没有持有许可证的情况下调用park方法而被阻塞挂起时,这个blocker对象会被记录到该线程内部。

使用诊断工具可以观察线程被阻塞的原因,诊断工具即是通过getBlocker(Thread)方法来获取blocker对象的,所以JDK推荐使用带有blocker参数的park方法,并且blocker被设置为this,这样当排查问题时就知道是哪个类被阻塞了(可以使用jstack pid查看线程堆栈)。