Розуміння java.lang.Thread.State: ОЧІКУВАННЯ (парковка)


90

По-перше, справді тупе запитання, мені просто цікаво, що означає очікування "паркування"? Чи нитка чекає на припаркування, чи вона просто припаркована і, отже, перебуває в стані очікування? І коли відбувається така парковка, скільки ресурсів процесора / пам'яті забирається? Яка мета паркування нитки?

По-друге, розглянувши метод паркування в API Java-потоку

Вимикає поточний потік для цілей планування потоків, якщо немає дозволу.

Якщо дозвіл є, тоді він споживається, і дзвінок негайно повертається; в іншому випадку поточний потік стає неактивним для планування потоків і залишається бездіяльним, поки не трапиться одна з трьох речей .....

Англійська мова не є моєю основною мовою, тому я відчуваю певні труднощі, розуміючи, що я мав на увазі "дозвіл" як "дозвіл на паркування нитки", тому наступні запитання:

  • що це означає, що таке «дозвіл», і хто і як перевіряє цей дозвіл?
  • Що це означає: "якщо дозвіл є, то він споживається", чи стає він "припаркованим"?
  • наступне, якщо другий пункт відповідає дійсності, то в чому різниця між "паркуванням" та "бездіяльністю"? Якщо у мене є дозвіл, я можу залишити його назавжди, а якщо ні, то можу зробити його «неактивним»?

Дякую

Відповіді:


36

Дозвіл означає дозвіл на продовження виконання. Стоянка означає призупинення виконання до отримання дозволу.

На відміну від Semaphoreдозволів, дозволи LockSupportпов'язані з потоками (тобто дозвіл дається певному потоку) і не накопичується (тобто може бути лише один дозвіл на потік, коли потік споживає дозвіл, він зникає).

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


2
Отже, переосмислюючи, якщо потік A викликає 'паркувати' для потоку B, але є дозвіл, тобто 'B не можна припаркувати', тоді дзвінок, зроблений A, просто повертається, а B не паркується. В іншому випадку, коли дозволу немає, Б повинен підкорятися. Отже, чи означає очікування (паркування) "А намагається припаркувати мене, бо я не маю дозволу, але я не можу цього зробити зараз, тому я теж блокую А"? Вибачте за це довге речення. Я вважаю, що це очікування досить ресурсомістке. Мені все ще цікаво, хто керує цією справою дозволу. Хто / що вирішує, що одні нитки мають дозвіл, а інші - ні.
Леонардо

2
@Leonardo: Потік може припаркуватись лише сам, інших потоків немає можливості припаркувати. Отже, виклик park()означає "Я хочу призупинити своє виконання, доки якийсь інший потік не дасть мені дозволу за допомогою виклику unpark()".
axtavt

Отже, потік не може паркувати інші потоки, але може бути відпаркований іншими потоками? Це правильно ? Отже, коли відбувається таке паркування? Можливо, нитка зараз не має роботи, і це спосіб перевірити її, постійно переглядаючи її дозвіл? Наприклад, це добре підійде для демонової нитки.
Леонардо,

Крім того, ОЧІКУВАННЯ (стоянка) означає, що воно чекає, щоб бути припаркованим, або воно перебуває в стані очікування після того, як припаркується? Вибачте, я знаю, що це німе запитання :-)
Леонардо

3
@ Леонардо: Це означає стан очікування після паркування.
axtavt

11

Відповідно до документації стану потоку Java , потік може перейти до стану ОЧІКУВАННЯ з трьох причин:

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

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

Отже, коли ваша Нитка перебуває в режимі ОЧІКУВАННЯ LockSupport.park, вона відображатиме Вас як ОЧІКУВАННЯ (паркування).

Зверніть увагу, що ви можете зателефонувати у парк лише на поточній нитці. Це дуже корисний механізм для впровадження шаблону дизайну виробника та споживача.


3

З опису класу (у верхній частині javadoc LockSupport ), де він описує дозвіл:

Цей клас асоціюється з кожним потоком, який його використовує, дозвіл (у розумінні класу Семафор). Виклик до паркування негайно повернеться, якщо дозвіл є, споживаючи [дозвіл] у процесі; інакше [виклик паркування] може заблокувати. Виклик до зняття парку робить дозвіл доступним, якщо він ще не був доступний. (Однак, на відміну від семафорів, дозволи не накопичуються. Є не більше одного.)

(Я розширив [текст], щоб полегшити читання для тих, хто не говорить англійською мовою.)

Сподіваємось, хтось із глибшим розумінням може це детально розробити. Дивіться відповідь 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.

Що стосується використання центрального процесора / пам'яті, я думаю, що це залежить від ОС тощо ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.