Différence entre les états de thread WAIT et BLOCKED

101

Quelle est la différence entre l'état de thread WAIT et l'état de thread BLOCKED?

La documentation Thread.State :

Bloqué
Un thread bloqué en attente d'un verrouillage du moniteur est dans cet état.

En attente
Un thread qui attend indéfiniment qu'un autre thread effectue une action particulière est dans cet état

ne m'explique pas la différence.

Plus de cinq
la source
vérifiez la réponse dans ce fil stackoverflow.com/questions/2534147/java-thread-wait-blocked également ce lien peut fournir des précisions geekexplains.blogspot.cz/2008/07/…
Abdul
@Abdul le lien geekexplains indique qu'un thread peut passer dans un état bloqué en appelant Object.wait () ce n'est pas correct, n'est-ce pas?
Plus de cinq
selon oracle docs docs.oracle.com/javase/6/docs/api/java/lang/… : Un thread est en état d'attente en raison de l'appel de l'une des méthodes suivantes: Object.wait sans délai d'expiration, Thread.join sans délai, LockSupport.park
Abdul
Pour mémoire, je pense que la réponse de @ Flavio est un peu meilleure que celle d'Ankit au cas où vous pourriez envisager de changer.
Gray

Réponses:

80

Un thread passe à l'état d'attente une fois qu'il appelle wait()un objet. C'est ce qu'on appelle l' état d' attente . Une fois qu'un thread atteint l'état d'attente, il devra attendre qu'un autre thread appelle notify()ounotifyAll() sur l'objet.

Une fois que ce thread est notifié, il ne sera pas exécutable. Il se peut que d'autres threads soient également notifiés (en utilisant notifyAll()) ou que le premier thread n'ait pas terminé son travail, il est donc toujours bloqué jusqu'à ce qu'il ait sa chance. C'est ce qu'on appelle l' état bloqué . Un état bloqué se produit chaque fois qu'un thread essaie d'acquérir un verrou sur un objet et qu'un autre thread détient déjà le verrou.

Une fois que d'autres threads sont partis et que c'est cette chance de thread, il passe à l'état Runnable après qu'il est éligible au travail de ramassage basé sur le mécanisme de thread JVM et passe à l'état d'exécution.

Ankit Bansal
la source
2
Vous l'avez expliqué beaucoup mieux parce que vous avez expliqué la séquence dans laquelle un thread atteint ces deux états, ce qui le rend plus clair que d'expliquer simplement chacun des deux états de manière isolée (ce qui est fait par la réponse de "Plus de cinq"
Kumar Manish
7
Pour tous ceux qui se demandent pourquoi la plupart (tous?) Des diagrammes d'état trouvés dans la revendication Web, que notify () / notifyAll () aboutit à RUNNABLE au lieu de BLOCKED: stackoverflow.com/questions/28378592/…
Niklas Peter
Supposons qu'il n'y ait qu'un seul thread et qu'il ait attendu un certain temps en millisecondes; maintenant Est-il possible qu'un thread puisse directement à partir de l'état d'attente pour passer à l'état exécutable? puisque aucun autre thread ne prend le verrouillage ici car un seul thread?
Kanagavelu Sugumar
Il existe une méthode wait (time) qui reviendra à l'état exécutable une fois le temps écoulé. Mais si aucune heure n'est spécifiée, il attendra jusqu'à ce qu'un autre thread notifie ou que le thread soit interrompu.
Ankit Bansal
2
Votre réponse est bonne, mais elle n'explique pas tout à fait que vous pouvez entrer dans un état bloqué à chaque fois que vous essayez d'acquérir un verrou. Cela n'a rien à voir avec le signal / notification.
Gray
90

La différence est relativement simple.

Dans l' BLOCKEDétat, un thread est sur le point d'entrer dans un synchronizedbloc, mais il y a un autre thread en cours d'exécution à l'intérieur d'un synchronizedbloc sur le même objet. Le premier thread doit ensuite attendre que le second thread quitte son bloc.

Dans l' WAITINGétat, un thread attend un signal d'un autre thread. Cela se produit généralement en appelant Object.wait()ou Thread.join(). Le thread restera alors dans cet état jusqu'à ce qu'un autre thread appelleObject.notify() ou meure.

Flavio
la source
2
est-il correct de dire que seul un fil lui-même peut le faire attendre? Thread-B peut-il jamais faire passer Thread-A à l'état WAIT?
Plus de cinq
1
Vous l'utilisez rarement Object.wait()directement, mais vous vous retrouvez dans l' WAITINGétat en utilisant également les constructions de concurrence de haut niveau - comme les verrous, les files d'attente de blocage, etc. en gros, chaque fois que deux threads doivent se coordonner.
Flavio
1
D'après l'expérience personnelle, les threads en attente d'E / S (par exemple la lecture à partir d'un socket) sont dans l' RUNNINGétat.
Flavio
4
La documentation Java8 pour Thread.Statedit: "... Ces états sont des états de machine virtuelle qui ne reflètent aucun état de thread du système d'exploitation." En d'autres termes, la JVM ne se soucie pas de la différence entre un thread qui exécute du code Java, un thread qui attend le retour d'un appel système ou un thread qui attend une tranche de temps. Tout cela RUNNABLEconcerne la JVM.
Solomon Slow
3
Il peut être intéressant d'ajouter que lorsqu'un thread passe de l' WAITINGétat, il doit d'abord passer à l' BLOCKEDétat jusqu'à ce qu'il puisse acquérir le verrou associé à l'objet qu'il attendait.
Gray
22

La différence importante entre les états bloqué et d'attente est l'impact sur le planificateur. Un thread dans un état bloqué est en compétition pour un verrou; ce thread compte toujours comme quelque chose que le planificateur a besoin de traiter, pouvant être pris en compte dans les décisions du planificateur sur le temps imparti aux threads en cours d'exécution (afin qu'il puisse donner une chance aux threads bloquant sur le verrou).

Une fois qu'un thread est en état d'attente, le stress qu'il met sur le système est minimisé et le planificateur n'a pas à s'en soucier. Il passe en veille jusqu'à ce qu'il reçoive une notification. À l'exception du fait qu'il garde un thread du système d'exploitation occupé, il est entièrement hors jeu.

C'est pourquoi l'utilisation de notifyAll est loin d'être idéale, cela provoque le réveil d'un tas de threads qui étaient auparavant heureusement dormants sans aucune charge sur le système, où la plupart d'entre eux se bloqueront jusqu'à ce qu'ils puissent acquérir le verrou, trouver la condition dans laquelle ils sont attendre n'est pas vrai, et recommencez à attendre. Il serait préférable de ne notifier que les threads qui ont une chance de progresser.

(L'utilisation de ReentrantLock au lieu de verrous intrinsèques vous permet d'avoir plusieurs conditions pour un verrou, afin que vous puissiez vous assurer que le thread notifié est celui qui attend une condition particulière, évitant ainsi le bogue de notification de perte dans le cas où un thread est notifié pour quelque chose sur lequel il ne peut pas agir.)

Nathan Hughes
la source
Est-ce parce qu'il est de la responsabilité d'autres threads d'appeler notify () sur l'objet moniteur?
berimbolo
@berimbolo: Je ne comprends pas ce que vous demandez
Nathan Hughes
Il s'agissait de savoir pourquoi un thread en attente n'est pas quelque chose dont le planificateur doit s'inquiéter. Je me suis demandé si c'était parce qu'un autre thread sera chargé d'appeler notifier s'il attend.
berimbolo
@berimbolo: le Thread en attente est finalement réveillé par une notification. Le planificateur déciderait quel thread en attente sera notifié.
Nathan Hughes
compte quelque chose, vous dites spin-lock, dose BLOCKED ne veut pas dire que c'est spin-lock
Frank Zhang
16

Perspective simplifiée pour l'interprétation des thread dumps:

  • ATTENDRE - J'attends qu'on me donne du travail, donc je suis inactif en ce moment.
  • BLOQUÉ - Je suis occupé à essayer de faire le travail, mais un autre thread se trouve sur mon chemin, donc je suis inactif en ce moment.
  • RUNNABLE ... (Native Method) - J'ai appelé à RUN un certain code natif (qui n'est pas encore terminé) donc en ce qui concerne la JVM, vous êtes RUNNABLE et il ne peut pas donner plus d'informations. Un exemple courant serait une méthode d'écoute de socket native codée en C qui attend en fait l'arrivée du trafic, donc je suis inactif pour le moment. Dans cette situation, cela peut être considéré comme un type particulier de WAIT car nous ne sommes pas en cours d'exécution (pas de brûlure du processeur) du tout, mais vous devrez utiliser un vidage de thread du système d'exploitation plutôt qu'un vidage de thread Java pour le voir.
vieil homme
la source
1
J'aime votre explication. C'est exactement ce que j'essaie de faire en analysant les décharges de threads en ce moment :)
Sridhar Sarnobat
@MuhammadGelbana Ouais, tu as raison, j'ai supprimé le commentaire.
Eric Wang
1
Vous n'avez RUNNABLEpas tout à fait raison. Il peut se trouver dans la file d'attente d'exécution Java mais ne pas s'exécuter ou il peut exécuter du code Java. Il n'est pas nécessaire de faire appel à la terre d'origine.
Gray
1

Bloqué - Votre thread est dans l'état exécutable du cycle de vie du thread et tente d'obtenir le verrouillage d'objet. Wait - Votre thread est en attente du cycle de vie du thread et attend que le signal de notification entre dans l'état exécutable du thread.

Prakash Bisht
la source
-1

voir cet exemple:

démonstration des états des threads.

/*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
la source
Merci pour le code, mais je préfère que vous ayez une réponse textuelle et que vous montriez un petit bloc de code.
Gray