Comprensión de java.lang.Thread.State: ESPERA (estacionamiento)

90

Primero, una pregunta realmente tonta, me preguntaba qué significa el 'estacionamiento' en espera. ¿El hilo está esperando ser estacionado o simplemente se estacionó y por lo tanto está en estado de espera? Y cuando ocurre ese estacionamiento, ¿cuántos recursos de cpu / memoria se toman? ¿Cuál es el propósito de estacionar un hilo?

En segundo lugar, observando el método de estacionamiento en la API de subprocesos de Java

Desactiva el hilo actual para fines de programación de hilos a menos que el permiso esté disponible.

Si el permiso está disponible, se consume y la llamada regresa inmediatamente; de lo contrario, el hilo actual se deshabilita para fines de programación del hilo y permanece inactivo hasta que suceda una de tres cosas ...

El inglés no es mi idioma principal, por lo que tengo algunas dificultades para entenderlo, tenía la intención de 'permitir' como una especie de 'permiso para aparcar el hilo', por lo que las preguntas que siguen:

  • ¿Cuál es el significado de eso, qué es 'permiso', y quién y cómo verifica esos permisos?
  • ¿Qué significa eso: 'si el permiso está disponible, entonces se consume', se está 'estacionado'?
  • A continuación, si el segundo punto es cierto, ¿cuál es la diferencia entre "aparcar" y "permanecer inactivo"? Si tengo permiso, puedo estacionarlo para siempre y, si no, puedo dejarlo 'inactivo'.

Gracias

Leonardo
fuente

Respuestas:

36

Permiso significa un permiso para continuar la ejecución. Estacionar significa suspender la ejecución hasta que el permiso esté disponible.

A diferencia Semaphorede los permisos, los permisos de LockSupportestán asociados con subprocesos (es decir, el permiso se otorga a un subproceso en particular) y no se acumula (es decir, solo puede haber un permiso por subproceso, cuando el subproceso consume el permiso, desaparece).

Puede dar permiso a un hilo llamando unpark(). Un hilo puede suspender su ejecución hasta que el permiso esté disponible (o se interrumpa el hilo, o expire el tiempo de espera, etc.) llamando park(). Cuando el permiso está disponible, el subproceso aparcado lo consume y sale de un park()método.

axtavt
fuente
2
Asumiendo que, si el hilo A llama a 'aparcar' para el hilo B, pero el permiso está disponible, que es 'B no se puede aparcar', entonces la llamada realizada por A simplemente regresa y B no está aparcado. De lo contrario, cuando no hay permiso disponible, B tiene que obedecer. Entonces, ¿la espera (estacionamiento) significa "A está tratando de estacionarme porque no tengo permiso pero no puedo hacerlo ahora, así que también estoy bloqueando A"? Perdón por esta larga oración. Supongo que esta espera consume muchos recursos. Todavía me pregunto quién se encarga de todo el asunto de los permisos. Quién / qué está decidiendo que algunos hilos tienen permiso, mientras que otros no.
Leonardo
2
@Leonardo: Un hilo solo puede estacionarse solo, no hay forma de estacionar otros hilos. Entonces, llamar park()significa "Quiero suspender mi ejecución hasta que algún otro hilo me dé un permiso llamando unpark()".
axtavt
Entonces, ¿un hilo no puede aparcar otros hilos, pero otros hilos pueden desempacarlo? Es eso correcto ? Entonces, ¿cuándo ocurre ese estacionamiento? ¿Quizás un hilo no tiene trabajo que hacer en este momento, y su forma de verificarlo es mirando constantemente su permiso? Esto sería adecuado para el hilo de demonio, por ejemplo.
Leonardo
Además, ¿ESPERA (estacionamiento) significa que está esperando ser estacionado, o está en un estado de espera después de haber sido estacionado? Lo siento, sé que esta es una pregunta tonta :-)
Leonardo
3
@Leonardo: Significa un estado de espera después de estar estacionado.
axtavt
11

Según la documentación del estado de subprocesos de Java , un subproceso puede pasar al estado EN ESPERA por tres razones:

  1. Object.wait sin tiempo de espera
  2. Thread.join sin tiempo de espera
  3. LockSupport.park

Cuando llama a un método de estacionamiento en un hilo, deshabilita el hilo para fines de programación de hilos a menos que el permiso esté disponible. Puede llamar al método unpark para hacer disponible el permiso para el hilo dado, si aún no estaba disponible.

Entonces, cuando su Thread esté en modo de ESPERA por LockSupport.park, lo mostrará como ESPERANDO (estacionamiento).

Tenga en cuenta que solo puede llamar al estacionamiento en el hilo actual. Este es un mecanismo muy útil para implementar el patrón de diseño productor-consumidor.

Badal
fuente
3

De la descripción de la clase (en la parte superior del javadoc LockSupport ) donde describe el permiso:

Esta clase asocia con cada hilo que la usa, un permiso (en el sentido de la clase Semaphore). Una llamada para estacionar regresará inmediatamente si el permiso está disponible, consumiendo [el permiso] en el proceso; de lo contrario, [la llamada para aparcar] puede bloquearse. Una llamada para cancelar el estacionamiento hace que el permiso esté disponible, si aún no lo estaba. (Sin embargo, a diferencia de los semáforos, los permisos no se acumulan. Hay como máximo uno).

(Expandí el [texto] para que sea más fácil de leer para quienes no hablan inglés).

Es de esperar que alguien con un conocimiento más profundo pueda desarrollar esto. Vea la respuesta de axtavt.

Como nota final, una cita final del javadoc:

Estos métodos están diseñados para usarse como herramientas para crear utilidades de sincronización de alto nivel y no son útiles en sí mismos para la mayoría de las aplicaciones de control de simultaneidad.

Charles Goodwin
fuente
3

La parte que me hizo volver a examinar esta pregunta que no pude evitar mientras leía la documentación fue:

Si el permiso está disponible, se consume y la llamada vuelve de inmediato ...

Entonces, cuando el permiso está "disponible", ¿ quién y cómo lo hace disponible para que pueda consumirse inmediatamente? De alguna manera fue trivial de descubrir:

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

El código habla por sí mismo, se threadestá ejecutando pero aún no se ha llamado LockSupport.park, mientras que otro hilo lo llama LockSupport.unpark, lo que hace que el permiso esté disponible. Después de eso llamamosLockSupport.park y eso regresa inmediatamente ya que el permiso está disponible.

Una vez que lo piensa, esto es un poco peligroso, si expone sus subprocesos a algún código que no controla y ese código llama LockSupport.unparkmientras lo hace park, es posible que no funcione.

Eugenio
fuente
Muy buen punto, habría pensado que una actividad de concesión de permisos, es decir, llamar a unpark (), solo es relevante cuando se estaciona un hilo.
Alfred Xiao
@AlfredXiao estuvo de acuerdo, esto es algo que también me ha sorprendido, pero creo que tiene sentido.
Eugene
1

Según tengo entendido, el "permiso" es solo un objeto que representa si un hilo se puede "desempacar" o no. Y esto lo verifica el propio Thread (o de JRE cuando intentas aparcar un Thread) Lo "se consume", entiendo que el permiso desaparece y el Thread no se desactiva.

Creo que debería aprender un poco más sobre el subproceso múltiple. Piense en ello como un dispensador de objetos llamado "permiso". Le dices a un Hilo que se estacione, y el Hilo revisa el dispensador, si hay un "permiso", el Hilo lo toma y se va (sin estacionamiento). Si no hay "permiso" en el dispensador, el Thread se estaciona hasta que haya un "permiso" disponible (y puede poner un "permiso" en el dispensador con unpark.

En cuanto al uso de CPU / memoria, creo que depende del sistema operativo, etc.

Vic
fuente