Diferença entre estados de thread WAIT e BLOCKED

101

Qual é a diferença entre o estado de thread WAIT e o estado de thread BLOCKED?

A documentação Thread.State :

Bloqueado
Um encadeamento que está bloqueado à espera de um bloqueio de monitor está neste estado.

Aguardando
Um encadeamento que está esperando indefinidamente por outro encadeamento para realizar uma ação específica está neste estado

não explica a diferença para mim.

Mais de cinco
fonte
verifique a resposta neste tópico stackoverflow.com/questions/2534147/java-thread-wait-blocked também este link pode fornecer mais esclarecimentos geekexplains.blogspot.cz/2008/07/…
Abdul
@Abdul o link geekexplains diz que um thread pode entrar em um estado bloqueado chamando Object.wait () que não está correto, certo?
Mais de cinco
de acordo com a documentação do oracle docs.oracle.com/javase/6/docs/api/java/lang/… : Um thread está no estado de espera devido à chamada de um dos seguintes métodos: Object.wait sem tempo limite, Thread.join sem tempo limite, LockSupport.park
Abdul
Só para constar, acho que a resposta de @Flavio é um pouco melhor do que a de Ankit, caso você possa pensar em mudar.
Gray

Respostas:

80

Um thread vai para o estado de espera quando chama wait()um objeto. Isso é chamado de estado de espera . Uma vez que um thread atinge o estado de espera, ele precisará esperar até que algum outro thread chame notify()ou notifyAll()no objeto.

Assim que este tópico for notificado, ele não poderá ser executado. Pode ser que outros tópicos também sejam notificados (usando notifyAll()) ou o primeiro tópico não terminou seu trabalho, então ele ainda está bloqueado até ter sua chance. Isso é chamado de estado bloqueado . Um estado Blocked ocorrerá sempre que um thread tenta adquirir o bloqueio no objeto e algum outro thread já está segurando o bloqueio.

Uma vez que outros encadeamentos saíram e sua chance de encadeamento, ele se move para o estado Executável depois que é elegível para pegar o trabalho com base no mecanismo de encadeamento da JVM e se move para o estado de execução.

Ankit Bansal
fonte
2
Você explicou isso muito melhor porque explicou a sequência em que um thread atinge esses dois estados, o que torna mais claro do que apenas explicar cada um dos dois estados isoladamente (o que é feito pela resposta de "More Than Five"
Kumar Manish
7
Para todos aqueles que se perguntam por que a maioria (todos?) Dos diagramas de estado encontrados na declaração da web, que notificar () / notificarAll () resulta em RUNNABLE em vez de BLOQUEADO: stackoverflow.com/questions/28378592/…
Niklas Peter
Suponha que haja apenas um thread e esperou algum tempo em milis; agora é possível que um thread possa passar diretamente do estado de espera para o estado executável? uma vez que nenhum outro segmento leva bloqueio aqui, pois apenas um único segmento?
Kanagavelu Sugumar
Existe um método wait (time) que voltará ao estado executável assim que o tempo tiver decorrido. Mas se nenhum tempo for especificado, ele irá esperar até que outro thread seja notificado ou o thread seja interrompido.
Ankit Bansal
2
Sua resposta é boa, mas não explica muito bem que você pode entrar no estado Bloqueado sempre que tentar adquirir um bloqueio. Não tem nada a ver com sinalizar / notificar.
Gray
90

A diferença é relativamente simples.

No BLOCKEDestado, um thread está prestes a entrar em um synchronizedbloco, mas há outro thread em execução dentro de um synchronizedbloco no mesmo objeto. O primeiro thread deve então esperar que o segundo thread saia de seu bloco.

No WAITINGestado, um segmento está aguardando um sinal de outro segmento. Isso normalmente acontece por meio de uma chamada Object.wait(), ou Thread.join(). O encadeamento permanecerá neste estado até que outro encadeamento chame Object.notify()ou morra.

Flavio
fonte
2
é correto dizer que apenas um thread em si pode fazê-lo entrar em espera? O Thread-B pode fazer o Thread-A ir para o estado WAIT?
Mais de cinco
1
Você raramente usa Object.wait()diretamente, mas acaba no WAITINGestado de usar também as construções de simultaneidade de alto nível - como bloqueios, filas de bloqueio, etc ... falando de maneira geral, sempre que dois threads precisam ser coordenados.
Flavio
1
Por experiência pessoal, os encadeamentos aguardando IO (por exemplo, leitura de um Socket) estão no RUNNINGestado.
Flavio
4
Java8 doc for Thread.Statediz, "... Esses estados são estados de máquina virtual que não refletem nenhum estado de thread do sistema operacional." Em outras palavras, a JVM não se preocupa com a diferença entre um encadeamento que está executando o código Java, um encadeamento que está aguardando o retorno de uma chamada do sistema ou um encadeamento que está aguardando um intervalo de tempo. Tudo isso RUNNABLEdiz respeito apenas à JVM.
Solomon Slow
3
Pode ser bom adicionar que quando um encadeamento se move do WAITINGestado, ele deve primeiro ir para o BLOCKEDestado até que possa adquirir o bloqueio associado ao objeto que estava esperando.
Gray
22

A diferença importante entre os estados de bloqueio e espera é o impacto no planejador. Um thread em um estado bloqueado está lutando por um bloqueio; esse thread ainda conta como algo que o planejador precisa atender, possivelmente sendo fatorado nas decisões do planejador sobre quanto tempo dar aos threads em execução (para que possa dar uma chance aos threads que bloqueiam o bloqueio).

Quando um thread está no estado de espera, o estresse que ele coloca no sistema é minimizado e o planejador não precisa se preocupar com isso. Ele fica inativo até receber uma notificação. Exceto pelo fato de que ele mantém um thread do sistema operacional ocupado, está totalmente fora de jogo.

É por isso que usar o notificar tudo é menos do que o ideal, ele faz com que um monte de threads que antes estavam adormecidos sem carga no sistema sejam ativados, onde a maioria deles irá bloquear até que possam adquirir o bloqueio, encontrar a condição em que estão esperar não é verdade e volte a esperar. Seria preferível notificar apenas os threads que têm chance de fazer progresso.

(Usar ReentrantLock em vez de bloqueios intrínsecos permite que você tenha várias condições para um bloqueio, para que você possa ter certeza de que o thread notificado está aguardando uma condição específica, evitando o bug de notificação perdida no caso de um thread sendo notificado por algo em que não pode agir.)

Nathan Hughes
fonte
É porque é responsabilidade de algum outro encadeamento chamar notificar () no objeto monitor?
berimbolo
@berimbolo: Não entendo o que você está perguntando
Nathan Hughes
Foi por isso que um thread de espera não é algo com que o planejador precisa se preocupar. Eu me perguntei se isso era porque outro thread será responsável por chamar notificar se estiver esperando.
berimbolo
@berimbolo: o Thread em espera acaba sendo despertado por uma notificação. O planejador decidiria qual thread em espera será notificado.
Nathan Hughes
conta alguma coisa, você está dizendo spin-lock, BLOQUEADO dose não significa que é spin-lock
Frank Zhang
16

Perspectiva simplificada para interpretar despejos de thread:

  • ESPERE - Estou esperando um trabalho, então estou ocioso agora.
  • BLOQUEADO - Estou ocupado tentando fazer o trabalho, mas outro fio está no meu caminho, então estou ocioso agora.
  • RUNNABLE ... (Native Method) - Chamei para RUN algum código nativo (que não terminou ainda), no que diz respeito ao JVM, você está RUNNABLE e ele não pode dar mais nenhuma informação. Um exemplo comum seria um método de ouvinte de soquete nativo codificado em C que está realmente esperando que qualquer tráfego chegue, então estou ocioso agora. Nessa situação, isso pode ser visto como um tipo especial de WAIT, já que não estamos realmente RUNNING (sem queima de CPU), mas você terá que usar um despejo de thread do SO em vez de um despejo de thread Java para vê-lo.
cara velho
fonte
1
Eu gosto da sua explicação. Isso é exatamente o que estou tentando fazer ao analisar os despejos de thread agora :)
Sridhar Sarnobat
@MuhammadGelbana Sim, você está certo, apaguei o comentário.
Eric Wang
1
Você RUNNABLEnão está certo. Pode estar na fila de execução do Java, mas não em execução, ou pode estar executando o código Java. Não precisa ser um chamado para a terra natal.
Gray
1

Blocked- Seu segmento está em estado executável do ciclo de vida do segmento e tentando obter bloqueio de objeto. Wait- Seu segmento está em estado de espera do ciclo de vida do segmento e aguardando o sinal de notificação para entrar no estado executável do segmento.

Prakash Bisht
fonte
-1

veja este exemplo:

demonstração dos estados dos fios.

/*NEW- thread object created, but not started.
RUNNABLE- thread is executing.
BLOCKED- waiting for monitor after calling wait() method.
WAITING- when wait() if called & waiting for notify() to be called.
  Also when join() is called.
TIMED_WAITING- when below methods are called:
 Thread.sleep
 Object.wait with timeout
 Thread.join with timeout
TERMINATED- thread returned from run() method.*/
public class ThreadBlockingState{

public static void main(String[] args) throws InterruptedException {
    Object obj= new Object();
    Object obj2 = new Object();
    Thread3 t3 = new Thread3(obj,obj2);
    Thread.sleep(1000);
    System.out.println("nm:"+t3.getName()+",state:"+t3.getState().toString()+
            ",when Wait() is called & waiting for notify() to be called.");
    Thread4 t4 = new Thread4(obj,obj2);
    Thread.sleep(3000);
    System.out.println("nm:"+t3.getName()+",state:"+t3.getState().toString()+",After calling Wait() & waiting for monitor of obj2.");
    System.out.println("nm:"+t4.getName()+",state:"+t4.getState().toString()+",when sleep() is called.");
}

}
class Thread3 extends Thread{
Object obj,obj2;
int cnt;
Thread3(Object obj,Object obj2){
    this.obj = obj;
    this.obj2 = obj2;
    this.start();
}

@Override
public void run() {
    super.run();
    synchronized (obj) {
        try {
            System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",Before Wait().");
            obj.wait();             
            System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",After Wait().");
            synchronized (obj2) {
                cnt++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
class Thread4 extends Thread{
Object obj,obj2;
Thread4(Object obj,Object obj2){
    this.obj = obj;
    this.obj2 = obj2;
    this.start();
}

@Override
public void run() {
    super.run();
    synchronized (obj) {
        System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",Before notify().");
        obj.notify();
        System.out.println("nm:"+this.getName()+",state:"+this.getState().toString()+",After notify().");
    }
    synchronized (obj2) {
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
murali
fonte
Obrigado pelo código, mas prefiro que você tenha uma resposta textual e, em seguida, mostre um pequeno bloco de código.
Gray