我们知道各种并发框架如CountDownLatch、CyclicBarrier和Semaphore是基于AQS (AbstractQueuedSynchronizer)框架实现的,AQS框架借助于两个类:

  • Unsafe(提供CAS操作) //JDK9以后引入了VarHandle变量句柄,代替了Unsafe
  • LockSupport(提供park/unpark操作)
  • 而LockSupport的park和unpark的实现是依赖于Unsafe类的prak和unpark的。重载方法中可以传入一个blocker对象,在dump线程时能获得更多的信息,用于问题排查或系统监控。

    public static void park() {
    U.park(false, 0L); //U = Unsafe.getUnsafe();
    }
    public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    U.park(false, 0L);
    setBlocker(t, null);
    }

    那么Unsafe类是个什么东西呢?Unsafe的全限定名是sun.misc.Unsafe。在源码的注释中我们可以看到:执行低级、不安全操作的方法集合。从名字就知道它是不安全的,它的功能有很多,比如:volatile的读写一个变量的field,有序的写一个变量的field,直接内存操作:申请内存,释放内存,CAS的修改变量等。

    本文主要说说park和unpark方法。最简单的理解:park阻塞一个线程,unpark唤醒一个线程。

    核心设计原理:“许可”。 park方法本质是消费许可,如果没有可消费的许可,那么就阻塞当前线程,一直等待,直到阻塞线程的unpark方法被其他线程调用,然后消费许可,当前线程被唤醒,继续执行。unpark方法本质是生产许可,一个线程刚创建出来,然后运行,此时是没有许可的,所以unpark方法可以在park方法前调用。下次park方法调用时,直接消费许可,线程不用阻塞等待许可。许可最多只有一个,连续多次调用unpark只能生产一个许可。

    park方法有几种重载的形式,可以设置等待时间,等待时间可以设置为绝对的或者相对的,超过等待时间,线程会自动被唤醒。

    底层实现原理:

    在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。
    mutex和condition保护了一个_counter的变量,简单点说:当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。
    LockSupport的park和unpark方法相比于Synchronize的wait和notify,notifyAll方法:
    1.更简单,不需要获取锁,能直接阻塞线程。
    2.更直观,以thread为操作对象更符合阻塞线程的直观定义;
    3.更精确,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程);
    4.更灵活 ,unpark方法可以在park方法前调用。