Понимание java.lang.Thread.State: WAITING (парковка)

91

Во-первых, действительно тупой вопрос, мне просто интересно, что означает ожидание «парковка»? Ожидает ли поток, чтобы его припарковали, или он просто припаркован и поэтому находится в состоянии ожидания? И когда эта парковка произойдет, сколько ресурсов процессора / памяти будет занято? Какова цель парковки потока?

Во-вторых, взглянув на метод park в API потока Java

Отключает текущий поток для целей планирования потоков, если разрешение не доступно.

Если разрешение доступно, оно потребляется, и вызов немедленно возвращается; в противном случае текущий поток будет отключен для целей планирования потоков и будет бездействовать, пока не произойдет одно из трех ...

Английский не является моим основным языком, поэтому у меня есть некоторые трудности с пониманием этого, я имел в виду «разрешение» как своего рода «разрешение на парковку темы», поэтому следующие вопросы:

  • что это означает, что такое «разрешение», и кто и как проверяет это разрешение?
  • Что это значит: «если разрешение есть, значит, оно израсходовано», «припарковано»?
  • Далее, если второй пункт верен, то в чем разница между «парковкой» и «бездействием»? Если у меня есть разрешение, я могу оставить его навсегда, а если нет, я могу сделать его «неактивным»?

благодаря

Леонардо
источник

Ответы:

37

Разрешение означает разрешение на продолжение выполнения. Парковка означает приостановку исполнения до получения разрешения.

В отличие от Semaphoreразрешений, разрешения LockSupportсвязаны с потоками (т.е. разрешение дается определенному потоку) и не накапливаются (т.е. может быть только одно разрешение на поток, когда поток использует разрешение, оно исчезает).

Вы можете дать разрешение потоку, позвонив unpark(). Поток может приостановить свое выполнение до тех пор, пока не будет доступно разрешение (или поток будет прерван, или истечет время ожидания и т. Д.) Путем вызова park(). Когда разрешение доступно, припаркованный поток потребляет его и выходит из park()метода.

axtavt
источник
2
Итак, предположим, что если поток A вызывает «парк» для потока B, но разрешение доступно, то есть «B не может быть припарковано», то вызов, сделанный A, просто возвращается, а B не запаркован. В противном случае, когда нет разрешения, B должен подчиняться. Итак, означает ли ожидание (парковка) «A пытается припарковать меня, потому что у меня нет разрешения, но я не могу сделать это прямо сейчас, поэтому я тоже блокирую A»? Извините за этот длинный приговор. Полагаю, это ожидание требует значительных ресурсов. Мне все еще интересно, кто управляет всем делом с разрешениями. Кто / что решает, что у некоторых потоков есть разрешение, а у других нет.
Леонардо
2
@Leonardo: поток может запарковаться только сам, нет возможности припарковать другие потоки. Таким образом, вызов park()означает: «Я хочу приостановить выполнение, пока какой-либо другой поток не даст мне разрешение путем вызова unpark()».
axtavt
Итак, поток не может припарковать другие потоки, но может быть отменен другими потоками? Это правильно ? Итак, когда происходит эта парковка? Может быть, потоку сейчас нечего делать, и это способ проверить, постоянно глядя на его разрешение? Это хорошо подходит, например, для потока демонов.
Леонардо
Кроме того, ОЖИДАНИЕ (парковка) означает, что он ожидает парковки или находится в состоянии ожидания после того, как был припаркован? Извините, я знаю, что это глупый вопрос :-)
Леонардо
3
@Leonardo: Это означает состояние ожидания после парковки.
axtavt
11

Согласно документации состояния потока Java , поток может перейти в состояние WAITING по трем причинам:

  1. Object.wait без тайм-аута
  2. Thread.join без тайм-аута
  3. LockSupport.park

Когда вы вызываете метод парковки в потоке, он отключает поток для целей планирования потоков, если разрешение не доступно. Вы можете вызвать метод unpark, чтобы сделать доступным разрешение для данного потока, если оно еще не было доступно.

Итак, когда ваш поток находится в режиме WAITING от LockSupport.park, он будет отображаться как WAITING (парковка).

Обратите внимание, что вы можете вызывать парковку только в текущем потоке. Это очень полезный механизм для реализации шаблона проектирования производитель-потребитель.

Бадал
источник
3

Из описания класса (вверху javadoc LockSupport ), где описывается разрешение:

Этот класс ассоциируется с каждым потоком, который его использует, разрешением (в смысле класса 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();
    }
}    

Код говорит сам за себя, threadон запущен, но еще не вызван LockSupport.park, в то время как какой-то другой поток вызывает LockSupport.unparkего, что делает разрешение доступным. После этого мы звоним, LockSupport.parkи он сразу же возвращается, так как разрешение доступно.

Если подумать, это немного опасно, если вы открываете свои потоки некоторому коду, который вы не контролируете, и этот код вызывает, LockSupport.unparkпока вы parkпосле этого - это может не сработать.

Евгений
источник
Очень хороший момент, я бы подумал, что действие, дающее разрешение, то есть вызов unpark (), актуально только тогда, когда поток припаркован.
Альфред Сяо
@AlfredXiao согласился, меня это тоже удивило, но, думаю, в этом есть смысл.
Евгений
1

Насколько я понимаю, «разрешение» - это просто объект, который представляет, может ли поток быть «незапаркованным» или нет. И это проверяется самим потоком (или de JRE, когда вы пытаетесь припарковать поток). «Потребляется», я понимаю, что разрешение исчезает, и поток не отключается.

Я думаю, вам следует узнать немного больше о многопоточности ... Думайте об этом как о распределителе с объектами под названием «разрешение». Вы говорите потоку запарковаться, а поток проверяет дозатор, если есть «разрешение», поток берет его и уходит (без парковки). Если в дозаторе нет «разрешения», поток остается на стоянке до тех пор, пока «разрешение» не станет доступным (и вы можете поместить «разрешение» в дозатор с помощью unpark.

Что касается использования ЦП / памяти, я думаю, это зависит от ОС и т. Д.

Вик
источник