LockSupport学习

LockSupport工具类定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。Java锁和同步器框架的核心工具类AQS:AbstractQueueSynchronizer,就是通过调用LockSupport.park()和LockSupport.unpark()实现线程的阻塞和唤醒。

LockSupport很类似于二元信号量(只有1个许可证可供使用),如果这个许可证还没有被占用,当前线程获取许可并继续执行。如果许可已经被占用,当前线程阻塞,等待获取许可。但是这个许可是不能叠加的,“许可”是一次性的。

比如线程B连续调用了三次unpark函数,当前线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。

注意:unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行。

park和unpark的灵活之处

上面已经提到了,unpark函数可以先于park调用,这个正是它们的灵活之处。

一个线程它有可能在别的线程unpark之前,或者之后,或者同时调用park,那么因为park的特性,它可以不用担心自己的park的时序问题,否则,如果park必须要在unpark之前,那么给编程带来很大的麻烦!!

考虑一下,两个线程同步,要如何处理?

在Java5里是用wait/notify/notifyAll来同步的。wait和notify机制有个很蛋疼的地方是,比如线程B要用notify通知线程A,那么线程B要确保线程A已经在wait调用上等待了,否则线程A可能永远都在等待。编程的时候就会很蛋疼。

另外,是调用notify,还是notifyALL?

notify只会唤醒一个线程,如果错误地有两个线程在同一个对象上wait等待,那么又悲剧了。为了安全起见,貌似只能调用nofityAll了。

park和unpark模型真正解耦了线程之间的同步,线程之间不再需要一个Objec或者其他变量来存储状态,不再需要关心对方的状态。

LockSupport API的使用

LockSupport提供的阻塞和唤醒方法
方法名 方法摘要
static void park() 阻塞当前线程,如果调用了unpark(Thread thread)方法或者当前线程被中断,才能park()方法返回
static void unpark() 唤醒处于阻塞状态的线程thread
static void parkNanos(long nanos) 阻塞当前线程,最长不超过nanos纳米,返回条件在park()的基础上增加了超时返回
static void parkUntil(long dealine) 在指定的时限前阻塞当前线程

LockSupport 简单使用

public static void main(String[] args) {

    Executor executor = Executors.newFixedThreadPool(2);

   Thread mainThread = Thread.currentThread() ;

    executor.execute(()->{
        try {
            Thread.sleep(3000L);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("start unpark main thread");
        LockSupport.unpark(mainThread);
        System.out.println("unpark main thread over");
    });

    System.out.println("before park");
    LockSupport.park();
    System.out.println("after park ");
}

运行结果:

先释放许可,再获取许可

@Test
public void test1() {
    Thread thread = Thread.currentThread();
    LockSupport.unpark(thread);//释放许可
    LockSupport.park();// 获取许可
    System.out.println("b");
}

中断响应

public static void main(String[] args) {

    Executor executor = Executors.newFixedThreadPool(2);

    Thread mainThread = Thread.currentThread() ;

    executor.execute(()->{
        try {
            Thread.sleep(3000L);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("start interrupt main thread");
        mainThread.interrupt();
        System.out.println("interrupt main thread over");
    });

    System.out.println("before park");
    LockSupport.park();
    LockSupport.park();
    System.out.println("after park ");
}

运行截图:

原理和本文参考

详见:Java的LockSupport.park()实现分析

posted @ 2017-09-03 11:11  做个有梦想的咸鱼  阅读(298)  评论(0编辑  收藏  举报