LockSupport

  JUC包下的阻塞原语,所谓阻塞原语指的是JUC下所有对线程挂起的操作都是通过LockSupport来完成的。

基本操作

  有点像wait notify机制,子线程调用park会被挂起,等待别的线程unpark才会接着park继续执行,且park的线程处于waiting状态。

public class testPark {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我要被park了");
                LockSupport.park();
                System.out.println("我又回来了");
            }
        });

        thread.start();
        Thread.sleep(1000);
        System.out.println(thread.getState());
        System.out.println("主线程来叫醒你");
        LockSupport.unpark(thread);
    }
}

中断

  处于waiting状态的线程中断标志位被改变的时候会退出waiting状态继续执行,但由于park而进入waiting状态的线程被中断的时候不会抛出异常,这一点和调用wait Sleep进入waiting状态不同,wait Sleep会抛出异常。

  

public class testPark {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我要被park了");
                LockSupport.park();
                System.out.println("我又回来了");
            }
        });

        thread.start();
        Thread.sleep(1000);
        System.out.println(thread.getState());
        System.out.println("主线程来叫醒你");
        //LockSupport.unpark(thread);
        thread.interrupt();
    }
}

简单原理  

  可以理解成信号量为1的互斥信号量_counter。调用park时,如果_counter为0,那么调用park的线程会被阻塞(不考虑超时),如果_counter为1,把_counter置为0并返回。调用unpark时,简单粗暴的把_counter置为1。

正是这种简单的逻辑,使的park和unpark的调用顺序和unpark的调用次数更加灵活。比如可以在park调用之前无限次调用unpark,因为unpark只是把_counter置为1。

  更加准确的说法是每一个调用park的线程都会检查该线程是否持有许可(permit),如果持有park就不会被阻塞,否则会被阻塞。这里的是否持有许可指的是_counter是否为1,如果为1就代表持有许可不会被阻塞。许可默认条件下不被线程持有,即_counter默认为0,只有先调用unpark才能申请许可,即调用unpark后_counter置为1。

假醒 

  java doc里写到,调用park的线程在没有许可的情况无法参与线程调度即阻塞直到下列情况发生 1、别的线程unpark 2、被中断 3、假醒spuriously 这是由于使用虚拟机使用C++实现park时一个Feature(或者bug??),总之告诉我们的一个信息是被park的线程可能在unpark之前醒过来,所以需要使用while把park包裹起来,并检查某些条件是否满足,如果条件不满足时被park的线程假醒需要再次park。

* <p>If the permit is available then it is consumed and the call
* returns immediately; otherwise the current thread becomes disabled
* for thread scheduling purposes and lies dormant until one of three
* things happens:
*
* <ul>
*
* <li>Some other thread invokes {@link #unpark unpark} with the
* current thread as the target; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
*
* <li>The call spuriously (that is, for no reason) returns.
* </ul>

blocker

  调用park方法时可传入一个对象让被park的线程阻塞?在blocker上,这样有利于使用jstack分析 

参考

  https://blog.csdn.net/hengyunabc/article/details/28126139

  《Java并发编程之美》

原文地址:https://www.cnblogs.com/AshOfTime/p/10859491.html