了解java.lang.Thread.State:等待(停车)


90

首先,一个非常愚蠢的问题,我只是想知道等待中的“停车”是什么意思?是线程正在等待驻留,还是线程刚刚处于驻留状态,因此处于等待状态?当发生停车时,会占用多少CPU /内存资源?存放线程的目的是什么?

其次,通过查看Java线程API中的park方法

除非有许可,否则出于线程调度目的禁用当前线程。

如果许可证可用,则将其消耗掉,并立即返回呼叫;否则,呼叫将立即返回。否则,当前线程将出于线程调度目的而被禁用,并处于休眠状态,直到发生三件事之一.....

英语不是我的主要语言,因此我很难理解,我打算将“许可”作为一种“允许线程驻留”,因此出现以下问题:

  • 这是什么意思,什么是“许可证”,以及谁以及如何检查这些许可证?
  • 这是什么意思:“如果有许可证,就被消耗掉了”,它被“停放了”吗?
  • 接下来,如果第二点是正确的,那么“停车”和“休眠”之间有什么区别?如果获得许可,我可以将其永久停放,如果没有,我可以将其“休眠”吗?

谢谢

Answers:


36

许可是指继续执行的许可。停车意味着中止执行直到获得许可为止。

Semaphore的许可不同,的许可LockSupport与线程相关联(即,将许可授予特定线程)并且不会累积(即,每个线程只能有一个许可,当线程消耗许可时,它就会消失)。

您可以通过调用来授予线程许可unpark()。线程可以通过调用挂起其执行,直到允许获得许可(或线程被中断或超时到期等)为止park()。当允许使用许可时,驻留的线程将使用它并退出park()方法。


2
因此,假设线程A调用了线程B的“驻留”,但允许,即“线程不能驻留”,则A进行的调用仅返回而B未被驻留。否则,当没有许可证时,B必须服从。那么,等待(停车)是否意味着“ A试图停车,因为我没有许可证,但现在无法这样做,所以我也封锁了A”?抱歉,这句话很长。我认为这种等待非常消耗资源。我仍然想知道是谁来管理整个许可证事务。谁/什么决定某个线程具有许可,而其他线程则没有。
莱昂纳多·

2
@Leonardo:一个线程只能自己驻留,而没有其他线程可以驻留。因此,调用的park()意思是“我想暂停执行,直到其他线程通过调用给我许可unpark()”。
axtavt 2011年

因此,一个线程不能驻留其他线程,但是可以被其他线程取消驻留吗?那是对的吗 ?那么,什么时候停车呢?也许线程目前没有工作要做,这是通过不断查看其许可来检查它的方法?例如,这将非常适合于守护程序线程。
莱昂纳多·

另外,WAITING(停车)意味着它正在等待停车,或者在停车之后处于等待状态?抱歉,我知道这是一个愚蠢的问题:-)
莱昂纳多·

3
@Leonardo:这意味着停放后处于等待状态。
axtavt 2011年

11

根据java线程状态文档,线程可以进入等待状态的原因有以下三个:

  1. Object.wait没有超时
  2. Thread.join没有超时
  3. LockSupport.park

当您在线程上调用停放方法时,除非有许可,否则它将出于线程调度目的禁用线程。您可以调用unpark方法为给定线程提供许可(如果尚未提供)。

因此,当LockSupport.park使您的线程处于WAITING模式时,它将显示为WAITING(停车)。

请注意,您只能在当前线程上调用驻留。这对于实现生产者-消费者设计模式非常有用。


3

从类描述(在LockSupport javadoc的顶部)描述许可的地方:

此类与使用它的每个线程相关联,一个许可(在Semaphore类的意义上)。如果有许可证,将立即返回停车请求,在此过程中消耗[许可证];否则[停车请求]可能会阻塞。取消停车的调用使许可证可用(如果尚不可用)。(不过与信号量不同,许可证不会累积。最多只能有一个。)

(我扩展了[文本],以使使用非英语的人更容易阅读。)

希望有更深入的了解的人可以对此进行阐述。请参阅axtavt的答案。

最后一点,来自Javadoc的最终报价:

这些方法旨在用作创建更高级别的同步实用程序的工具,而对于大多数并发控制应用程序本身并不有用。


3

让我重新审视这个在阅读文档时无法解决的问题的部分是:

如果有许可证,则将其消耗掉,并立即返回呼叫...

因此,许可证“可用”时,以及如何使它可用,以便可以立即使用它?这在某种程度上是微不足道的:

public static void main(String[] args) {

    Thread parkingThread = new Thread(() -> {
        System.out.println("Will go to sleep...");
        sleepTwoSeconds();
        System.out.println("Parking...");
        // this call will return immediately since we have called  LockSupport::unpark
        // before this method is getting called, making the permit available
        LockSupport.park();
        System.out.println("After parking...");
    });

    parkingThread.start();

    // hopefully this 1 second is enough for "parkingThread" to start
    // _before_ we call un-park
    sleepOneSecond();
    System.out.println("Un-parking...");
    // making the permit available while the thread is running and has not yet
    // taken this permit, thus "LockSupport.park" will return immediately
    LockSupport.unpark(parkingThread);

}

private static void sleepTwoSeconds() {
    try {
        Thread.sleep(1000 * 2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static void sleepOneSecond() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}    

该代码说明了一切,thethread正在运行但尚未调用LockSupport.park,而其他一些线程LockSupport.unpark在其上调用-因此使该许可可用。之后我们打电话LockSupport.park,因为有许可证就立即返回。

一旦考虑了一下,这将有些危险,如果将线程暴露给您无法控制的代码,LockSupport.unpark并且park此后调用该代码,则可能无法正常工作。


很好的一点是,我认为授予许可证的活动(即调用unpark())仅在线程被驻留时才有意义。
阿尔弗雷德·肖

@AlfredXiao同意,这也让我感到惊讶,但是我想这是有道理的。
尤金

1

据我了解,“许可”只是一个对象,它表示线程是否可以“不停放”。这是由线程本身检查的(或者当您尝试驻留线程时是de JRE)。“被消耗”的事情,我知道许可消失了,线程也没有被禁用。

我认为您应该了解有关多线程的更多知识。可以将其视为带有对象的分配器,称为“许可”。您告诉线程停放,然后线程检查分配器,如果有“许可证”,线程将其取走并离开(不停放)。如果分配器中没有“许可证”,则将线程停放,直到有“许可证”可用为止(您可以将“许可证”放入分配器中,unpark

至于CPU /内存使用情况,我认为这取决于操作系统等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.