LockSupport

简介: 介绍LockSupport的功能,主要方法。 介绍挂起、阻塞的区别


一 简介

1  概述

LockSupport是用于创建锁和其他同步类的阻塞原语。以下是jdk对LockSupport的描述。

Basic thread blocking primitives for creating locks and other synchronization classes.

在《ReentrantLock详解》(地址:https://yq.aliyun.com/articles/460711)中分析源码的时候,我们就已经多次提到使用LockSupport的pack挂起线程,unpack唤醒被挂起的线程,此博客将详述LockSupport的原理以及实现。

2  许可

LockSupport通过许可(permit)实现挂起线程、唤醒挂起线程功能。可以按照以下逻辑理解:

1)    pack

如果线程的permit存在,那么线程不会被挂起,立即返回;如果线程的permit不存在,认为线程缺少permit,所以需要挂起等待permit。

2)    unpack

如果线程的permit不存在,那么释放一个permit。因为有permit了,所以如果线程处于挂起状态,那么此线程会被线程调度器唤醒。如果线程的permit存在,permit也不会累加,看起来想什么事都没做一样。注意这一点和Semaphore是不同的。

 

二 源码分析

1  park

主要功能:

如果许可存在,那么将这个许可使用掉,并且立即返回。如果许可不存在,那么挂起当前线程,直到以下任意一件事情发生:

  • 其他线程以当前线程为参数,调用unpark(thread)方法
  • 其他线程通过Thread#interrupt中断当前线程。
  •  Pack方法没有原因的返回(调用时应该重新检查导致线程暂停的条件)。这一点后面解释。

    public static void park(Object blocker) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //设置线程的blocker对象
        setBlocker(t, blocker);
        //通过UNSAFE调用,挂起线程
        UNSAFE.park(false, 0L);
        //挂起的线程被唤醒以后,需要将阻塞的Blocker清理掉。
        setBlocker(t, null);
    }

 

 

2  pack的多版本

LockSupport中pack有多个版本,如下所示:

park(Object)

挂起当前线程,具体见上面pack的源码分析

parkNanos(Object, long)

指定了一个挂起时间(相对于当前的时间),时间到后自动被唤醒;例如1000纳秒后自动唤醒

parkUntil(Object, long)

指定一个挂起时间(绝对时间),时间到后自动被唤醒;例如2018-02-12 21点整自动被唤醒。

park()

和park(Object)相比少了挂起前为线程设置blocker、被唤醒后清理blocker的操作。

parkNanos(long)

和parkNanos(Object, long)类似,仅少了blocker相关的操作

parkUntil(long)

和parkUntil(Object, long)类似,仅少了blocker相关的操作

从上面表格可以看出,park支持blocker对象作为参数。此blocker对象在线程受阻塞时被记录,这样监视工具和诊断工具就可以确定线程受阻塞的原因。建议最好使用这些带blocker的方法版本,而不是不带blocker参数的方法。

 

3  unpark

设置线程许可为可用。

  • 如果线程当前已经被pack挂起,那么这个线程将会被唤醒。
  • 如果线程当前没有被挂起,那么下次调用pack不会挂起线程。

    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

 

三 分析

1  挂起与阻塞

挂起与阻塞主要的区别应该说是它们面向的对象不同。对线程来说, LockSupport的park/unpark更符合阻塞和唤醒的语义,他们以“线程”作为方法的参数,语义更清晰,使用起来也更方便。而wait/notify使“线程”的阻塞/唤醒对线程本身来说是被动的,要准确的控制哪个线程、什么时候阻塞/唤醒是很困难的,所以是要么随机唤醒一个线程(notify)要么唤醒所有的(notifyAll)。

park和unpark方法不会出现Thread.suspend和Thread.resume的死锁问题。这是因为许可的存在,调用park的线程和另一个试图将其unpark的线程之间的将没有竞争关系。此外,如果线程被中断或者超时,则park将返回。

park方法还可以在其他任何时间“毫无理由”地返回,因此通常必须在重新检查返回条件的循环里调用此方法。从这个意义上说,park 是“忙碌等待”的一种优化,它不会浪费这么多的时间进行自旋,但是必须将它与unpark配对使用才更高效。

 

2  使用模型

以下伪代码是pack的常用模型。

   while ( !enable() ) {
        //其他业务逻辑
        ......
        LockSupport.park(this);
        //其他业务逻辑
        ......
    }

 

以下是ReentrantLock中pack的使用代码


    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
 
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
 


相关文章
|
Java API
LockSupport与线程中断
LockSupport与线程中断
|
安全 Java
JUC第十一讲:JUC锁LockSupport详解
JUC第十一讲:JUC锁LockSupport详解
139 0
|
Java API
【JUC基础】09. LockSupport
LockSupport是一个线程阻塞工具,可以在线程任意位置让线程阻塞。线程操作阻塞的方式其实还有Thread.suspend()和Object.wait()。而LockSupport与suspend()相比,弥补了由于resume()方法而导致线程被挂起(类似死锁)的问题,也弥补了wait()需要先获得某个对象锁的问题,也不会抛出InterruptedException异常。
|
安全 Java C++
JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)
JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)
223 1
JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)
|
Java 程序员 API
JUC - 线程中断与线程等待、唤醒(LockSupport)
JUC - 线程中断与线程等待、唤醒(LockSupport)
JUC - 线程中断与线程等待、唤醒(LockSupport)
|
安全
【JUC】JDK1.8源码分析之LockSupport(一)
  最开始打算分析ReentrantLock,但是分析到最后,发现离不开LockSuport的支持,所以,索性就先开始分析LockSupport,因为它是锁中的基础,是一个提供锁机制的工具类,所以先对其进行分析。
126 0
【JUC】JDK1.8源码分析之LockSupport(一)
|
安全
JUC CyclicBarrier和Semphore
JUC CyclicBarrier和Semphore
104 0