线程阻塞工具类:LockSupport

160 阅读2分钟

介绍

LockSupport可以在线程内任意位置让线程阻塞,定义了一组公共静态方法提供最基本的线程阻塞和唤醒功能。

LockSupport类使用类似信号量的机制,LockSupport.unpark()方法使得一个许可变为可用,阻塞的park()方法发现许可可用会立即返回并且会将许可变为不可用。

和信号量不同的是它为每个线程准备一个许可,当执行unpark()方法时,执行一次和执行多次效果是一样的,不会累加可用许可的数量,只会使一个许可可用。由于这种特性,则即使unpark()方法发生在park()方法之前也可正常执行。

LockSupport类静态方法

相关静态方法如下:

//阻塞当前线程(当调用unpark(Thread thread)或者当前线程被中断才返回)
public static void park() 

//阻塞当前线程,增加了超时饭回,不超过nanos纳秒
public static void parkNanos(long nanos)

//阻塞当前线程,直到deadline时间(从1970年开始到deadline时间的毫秒数)
public static void parkUntil(long deadline) 

//唤醒处于阻塞状态的线程thread
public static void unpark(Thread thread) 

在java 6中新增了另外的三个park方法,多了一个类型为Object的blocker参数,用来标识当前线程等待的对象:

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

上面三个方法的增加主要为了便于问题排查和监控。我们可以通过线程dump结果可以定位到阻塞线程的对象。

简单示例

public class LockSupportDemo {
    public static final Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");

    public static class ChangeObjectThread extends Thread {
        public ChangeObjectThread(String name){
            super.setName(name);
        }
        @Override
        public void run() {
            synchronized (u){
                System.out.println("in " + getName());
                LockSupport.park();
                if(Thread.interrupted()){
                    System.out.println(getName() + " 被中断了" );
                }
                System.out.println(getName() + "执行结束");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //代码片段1
       /* t1.start();
        Thread.sleep(100);
        t2.start();
        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();*/

        //代码片段2
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.interrupt();
        LockSupport.unpark(t2);


    }
}

上述z执行代码片段1输出如下:

in t1
t1执行结束
in t2
t2执行结束

这个例子可以看出,我们无法保证unpark()方法发生在park方法之后,但是这段代码可正常执行。说明unpark方法可发生在park之前。

执行代码片段2会输出:

in t1
t1 被中断了
t1执行结束
in t2
t2执行结束

可以看出LockSupport.park()方法支持中断影响,但该方法不会抛出InterruptedException异常,只是默默返回。并且我们从中断后Thread.interrupted()为true可以知道park方法处理中断也并没有清除中断标识。

参考书籍:《Java高并发程序设计(第2版)》《Java并发编程实战》《Java并发编程的艺术》