Capire java.lang.Thread.State: WAITING (parking)


90

Innanzitutto, una domanda davvero stupida, mi chiedevo solo cosa significhi il "parcheggio" in attesa? Il thread è in attesa di essere parcheggiato o è stato appena parcheggiato e quindi è in stato di attesa? E quando si verifica il parcheggio, quante risorse della CPU / memoria vengono impiegate? Qual è lo scopo di parcheggiare un thread?

In secondo luogo, esaminando il metodo park nell'API java thread

Disabilita il thread corrente per scopi di pianificazione del thread a meno che il permesso non sia disponibile.

Se il permesso è disponibile, viene consumato e la chiamata torna immediatamente; altrimenti il ​​thread corrente viene disabilitato per scopi di pianificazione del thread e rimane inattivo fino a quando non accade una delle tre cose .....

L'inglese non è la mia lingua principale, quindi ho qualche difficoltà a capirlo, intendevo "permesso" come una sorta di "permesso per parcheggiare il thread", quindi le domande che seguono:

  • qual è il significato di questo, cos'è "permesso" e chi e come sta controllando quei permessi?
  • Cosa significa: "se il permesso è disponibile, allora viene consumato", viene "parcheggiato"?
  • di seguito, se il secondo punto è vero, qual è la differenza tra "parcheggio" e "è dormiente"? Se ho il permesso posso parcheggiarlo per sempre e se no, posso renderlo "dormiente"?

Grazie

Risposte:


36

Permesso significa un permesso per continuare l'esecuzione. Parcheggiare significa sospendere l'esecuzione fino a quando non sarà disponibile il permesso.

A differenza Semaphoredei permessi di, i permessi di LockSupportsono associati ai thread (cioè il permesso è dato a un particolare thread) e non si accumula (cioè ci può essere solo un permesso per thread, quando il thread consuma il permesso, scompare).

Puoi dare il permesso a un thread chiamando unpark(). Un thread può sospendere la sua esecuzione fino a quando non è disponibile il permesso (o il thread viene interrotto, o il timeout è scaduto, ecc.) Chiamando park(). Quando il permesso è disponibile, il thread parcheggiato lo consuma ed esce da un park()metodo.


2
Quindi, riassumendo, se il thread A chiama "park" per il thread B, ma è disponibile il permesso, che è "B non può essere parcheggiato", la chiamata effettuata da A ritorna e B non viene parcheggiata. Altrimenti, quando non è disponibile alcun permesso, B deve obbedire. Quindi, l'attesa (parcheggio) significa "A sta cercando di parcheggiarmi perché non ho il permesso ma non posso farlo in questo momento, quindi sto bloccando A"? Scusa per questa lunga frase. Suppongo che questa attesa stia consumando abbastanza risorse. Mi chiedo ancora chi gestisce l'intera faccenda dei permessi. Chi / cosa sta decidendo che alcuni thread hanno il permesso, mentre altri no.
Leonardo

2
@Leonardo: un thread può parcheggiare solo se stesso, non c'è modo di parcheggiare altri thread. Quindi, chiamare park()significa "Voglio sospendere la mia esecuzione fino a quando qualche altro thread non mi dà un permesso chiamando unpark()".
axtavt

Quindi un thread non può parcheggiare altri thread, ma può essere sbloccato da altri thread? È corretto ? Allora, quando si verifica quel parcheggio? Forse un thread non ha lavoro da fare al momento, ed è un modo per controllarlo guardando costantemente il suo permesso? Questo sarebbe adatto per il thread daemon, ad esempio.
Leonardo

Inoltre, WAITING (parcheggio) significa che sta aspettando di essere parcheggiato o è in uno stato di attesa dopo essere stato parcheggiato? Scusa lo so che questa è una domanda stupida :-)
Leonardo

3
@ Leonardo: Significa uno stato di attesa dopo essere stato parcheggiato.
axtavt

11

Secondo la documentazione sullo stato del thread java , un thread può passare allo stato WAITING per tre motivi:

  1. Object.wait senza timeout
  2. Thread.join senza timeout
  3. LockSupport.park

Quando chiami un metodo park su un thread, disabilita il thread per scopi di pianificazione del thread a meno che il permesso non sia disponibile. È possibile chiamare il metodo unpark per rendere disponibile il permesso per il thread specificato, se non era già disponibile.

Quindi, quando il tuo thread è in modalità WAITING da LockSupport.park, ti ​​mostrerà come WAITING (parcheggio).

Tieni presente che puoi chiamare il parcheggio solo sul thread corrente. Questo è un meccanismo molto utile per implementare il modello di progettazione produttore-consumatore.


3

Dalla descrizione della classe (nella parte superiore del javadoc LockSupport ) dove descrive il permesso:

Questa classe associa a ogni thread che la utilizza, un permesso (nel senso della classe Semaphore). Una chiamata al parcheggio tornerà immediatamente se il permesso è disponibile, consumando [il permesso] nel processo; altrimenti [la chiamata al parcheggio] potrebbe bloccarsi. Una chiamata per unpark rende disponibile il permesso, se non era già disponibile. (A differenza dei semafori, però, i permessi non si accumulano. Ce n'è al massimo uno.)

(Ho ampliato il [testo] per renderlo più facile da leggere per chi non parla inglese.)

Si spera che qualcuno con una comprensione più profonda possa approfondire questo aspetto. Vedi la risposta di axtavt.

Come nota finale, una citazione finale dal javadoc:

Questi metodi sono progettati per essere utilizzati come strumenti per la creazione di utilità di sincronizzazione di livello superiore e di per sé non sono utili per la maggior parte delle applicazioni di controllo della concorrenza.


3

La parte che mi ha fatto rivisitare questa domanda che non ho potuto aggirare leggendo la documentazione, è stata:

Se il permesso è disponibile, viene consumato e la chiamata torna immediatamente ...

Quindi quando il permesso è "disponibile", chi e come lo mette a disposizione, in modo che possa essere consumato immediatamente? Questo è stato in qualche modo banale da scoprire:

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();
    }
}    

Il codice parla da solo, threadè in esecuzione ma non ancora chiamato LockSupport.park, mentre qualche altro thread lo chiama LockSupport.unpark, rendendo così disponibile il permesso. Dopodiché chiamiamo LockSupport.parke quello ritorna immediatamente poiché il permesso è disponibile.

Una volta che ci pensi, questo è un po 'pericoloso, se esponi i tuoi thread a un codice che non controlli e quel codice chiama LockSupport.unparkmentre lo fai parkdopo, potrebbe non funzionare.


Ottimo punto, avrei pensato che un'attività di concessione di autorizzazioni, ad esempio la chiamata di unpark (), è rilevante solo quando un thread viene parcheggiato.
Alfred Xiao

@AlfredXiao è d'accordo, questo è qualcosa che ha sorpreso anche me, ma ha senso, immagino.
Eugene

1

A quanto ho capito, il "permesso" è solo un oggetto che rappresenta se un thread può essere "sbloccato" o meno. E questo viene controllato dal Thread stesso (o da JRE quando provi a parcheggiare un Thread) La cosa "è consumata", capisco che il permesso scompare e il Thread non è disabilitato.

Penso che dovresti imparare un po 'di più sul multithreading .. Pensalo come un distributore di oggetti chiamato "permesso". Dite a un Filo di parcheggiare, e il Filo controlla il distributore, se c'è un "permesso", il Filo lo prende e se ne va (senza parcheggio). Se non c'è un "permesso" nel distributore, il thread viene parcheggiato fino a quando non è disponibile un "permesso" (e puoi inserire un "permesso" nel distributore con unpark.

Per quanto riguarda l'utilizzo della CPU / memoria, penso che dipenda dal sistema operativo, ecc ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.