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

91

Primeiro, uma pergunta realmente idiota, eu estava me perguntando o que o 'estacionamento' em espera significa? O thread está esperando para ser estacionado ou acabou de ser estacionado e, portanto, está no estado de espera? E quando esse estacionamento acontecer, quantos recursos de CPU / memória serão usados? Qual é o propósito de estacionar um fio?

Em segundo lugar, olhando para o método park na API de thread Java

Desativa o thread atual para fins de agendamento de thread, a menos que a permissão esteja disponível.

Se a licença estiver disponível, ela é consumida e a chamada retorna imediatamente; caso contrário, o thread atual será desabilitado para fins de agendamento de thread e ficará inativo até que uma das três coisas aconteça ...

Inglês não é meu idioma principal, então tenho algumas dificuldades em entender que, eu pretendia 'permitir' como uma espécie de 'permissão para estacionar o fio', então as perguntas a seguir:

  • qual é o significado disso, o que é 'licença' e quem e como está verificando essas licenças?
  • O que isso significa: 'se a licença estiver disponível, então ela será consumida', está ficando 'estacionada'?
  • a seguir, se o segundo ponto for verdadeiro, então qual é a diferença entre 'estacionar' e 'ficar dormente'? Se eu tiver permissão, posso estacioná-lo para sempre e, se não, posso deixá-lo 'inativo'?

obrigado

Leonardo
fonte

Respostas:

37

Permitir significa uma permissão para continuar a execução. O estacionamento significa suspender a execução até que a licença esteja disponível.

Ao contrário Semaphoredas permissões de, as permissões de LockSupportestão associadas a threads (isto é, a permissão é dada a uma thread em particular) e não se acumulam (ou seja, pode haver apenas uma permissão por thread, quando a thread consome a permissão, ela desaparece).

Você pode permitir um tópico chamando unpark(). Um encadeamento pode suspender sua execução até que a permissão esteja disponível (ou o encadeamento seja interrompido ou o tempo limite expirado, etc.) chamando park(). Quando a permissão está disponível, o thread estacionado a consome e sai de um park()método.

axtavt
fonte
2
Assumindo então, se o thread A chamar 'estacionar' para o thread B, mas permitir estiver disponível, que é 'B não pode ser estacionado', então a chamada feita por A simplesmente retorna e B não é estacionado. Caso contrário, quando nenhuma licença estiver disponível, B tem que obedecer. Então, a espera (estacionamento) significa "A está tentando me estacionar porque não tenho permissão, mas não posso fazer isso agora, então estou bloqueando A também"? Desculpe por esta longa frase. Suponho que essa espera consome muitos recursos. Ainda estou me perguntando quem está gerenciando toda essa coisa de licença. Quem / o que está decidindo que algum tópico tem permissão, enquanto outros não.
Leonardo
2
@Leonardo: Um thread só pode se estacionar, não há como estacionar outros threads. Portanto, chamar park()significa "Quero suspender minha execução até que algum outro encadeamento me dê uma permissão chamando unpark()".
axtavt
Portanto, um thread não pode estacionar outros threads, mas pode ser desestacionado por outros threads? Isso está correto? Então, quando esse estacionamento ocorre? Talvez um segmento não tenha trabalho a fazer no momento, e sua maneira de verificá-lo é olhando constantemente para sua licença? Isso seria adequado para thread daemon, por exemplo.
Leonardo
Além disso, WAITING (estacionamento) significa que está aguardando para ser estacionado ou está em um estado de espera após ser estacionado? Desculpe, eu sei que esta é uma pergunta idiota :-)
Leonardo
3
@Leonardo: Significa um estado de espera após o estacionamento.
axtavt
11

De acordo com a documentação do estado do thread java , um thread pode ir para o estado WAITING por três motivos:

  1. Object.wait sem tempo limite
  2. Thread.join sem tempo limite
  3. LockSupport.park

Quando você chama um método de estacionamento em um Thread, ele desativa o thread para fins de agendamento de thread, a menos que a permissão esteja disponível. Você pode chamar o método unpark para disponibilizar a permissão para o encadeamento fornecido, se ainda não estiver disponível.

Portanto, quando seu Thread estiver no modo WAITING por LockSupport.park, ele mostrará como WAITING (parking).

Observe que você só pode chamar o estacionamento no Tópico atual. Este é um mecanismo muito útil para implementar o Padrão de Design Produtor-Consumidor.

Badal
fonte
3

Na descrição da classe (no topo do javadoc LockSupport ), onde descreve a licença:

Esta classe associa a cada thread que a usa, uma permissão (no sentido da classe Semaphore). Uma chamada para estacionar retornará imediatamente se a licença estiver disponível, consumindo [a licença] no processo; caso contrário, [a chamada para estacionar] pode bloquear. Uma chamada para desestacionar disponibiliza a licença, se ainda não estiver disponível. (Ao contrário dos semáforos, as licenças não se acumulam. Há no máximo um.)

(Eu expandi o [texto] para torná-lo mais fácil de ler para quem não fala inglês.)

Esperançosamente, alguém com uma compreensão mais profunda pode elaborar sobre isso. Veja a resposta de axtavt.

Como nota final, uma citação final do javadoc:

Esses métodos são projetados para serem usados ​​como ferramentas para a criação de utilitários de sincronização de alto nível e não são úteis em si mesmos para a maioria dos aplicativos de controle de simultaneidade.

Charles Goodwin
fonte
3

A parte que me fez revisitar essa questão que não consegui contornar ao ler a documentação foi:

Se a licença estiver disponível, ela é consumida e a chamada retorna imediatamente ...

Então, quando a licença está "disponível", quem e como a disponibiliza, para que possa ser consumida imediatamente? Isso foi de alguma forma trivial de descobrir:

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

O código fala por si mesmo, o threadestá em execução, mas ainda não foi chamado LockSupport.park, enquanto alguma outra thread o chama LockSupport.unpark- tornando a permissão disponível. Depois ligamos LockSupport.parke ele retorna imediatamente, pois a licença está disponível.

Depois de pensar sobre isso, isso é um pouco perigoso, se você expõe seus threads a algum código que você não controla e esse código chama LockSupport.unparkenquanto você parkdepois disso - pode não funcionar.

Eugene
fonte
Muito bom ponto, eu teria pensado que uma atividade de concessão de permissão - ou seja, chamar unpark () - só é relevante quando um thread está sendo estacionado.
Alfred Xiao
@AlfredXiao concordou, isso é algo que me surpreendeu também, mas meio que faz sentido, eu acho.
Eugene,
1

Pelo que entendi, a "licença" é apenas um objeto que representa se um Thread pode ser "desestacionado" ou não. E isso é verificado pelo próprio Thread (ou de JRE quando você tenta estacionar um Thread) A coisa "está consumido", eu entendo que a licença desaparece e o Thread não é desabilitado.

Acho que você deveria aprender um pouco mais sobre multithreading. Pense nisso como um dispensador com objetos chamados "permitir". Você diz a um Thread para estacionar, e o Thread verifica o dispensador, se houver uma "licença", o Thread pega e sai (sem estacionar). Se não houver "permissão" no dispensador, o Thread fica estacionado até que uma "licença" esteja disponível (e você pode colocar uma "licença" no dispensador com unpark.

Quanto ao uso de CPU / memória, acho que depende do SO, etc ...

Vic
fonte