Różnica między stanami wątku WAIT i BLOCKED

102

Jaka jest różnica między stanem wątku WAIT a stanem wątku ZABLOKOWANY?

Dokumentacja Thread.State :

Zablokowany
Wątek, który jest zablokowany i oczekuje na blokadę monitora, jest w tym stanie.

Oczekiwanie
W tym stanie jest wątek, który czeka w nieskończoność na wykonanie określonej czynności przez inny wątek

nie wyjaśnia mi różnicy.

Więcej niż pięć
źródło
sprawdź odpowiedź w tym wątku stackoverflow.com/questions/2534147/java-thread-wait-blocked również ten link może dostarczyć dalszych wyjaśnień geekexplains.blogspot.cz/2008/07/…
Abdul
@Abdul link geekexplains mówi, że wątek może przejść w stan zablokowany, wywołując Object.wait (), co jest niepoprawne, prawda?
Więcej niż pięć
według oracle docs docs.oracle.com/javase/6/docs/api/java/lang/… : Wątek jest w stanie oczekiwania z powodu wywołania jednej z następujących metod: Object.wait with no timeout, Thread.join bez limitu czasu, LockSupport.park
Abdul
Dla przypomnienia, myślę, że odpowiedź @ Flavio jest nieco lepsza niż Ankit, na wypadek gdybyś rozważał zmianę.
Gray

Odpowiedzi:

80

Wątek przechodzi w stan oczekiwania po wywołaniu wait()obiektu. Nazywa się to stanem oczekiwania . Gdy wątek osiągnie stan oczekiwania, będzie musiał poczekać, aż inne wywołania wątku notify()lub notifyAll()obiekt.

Po powiadomieniu tego wątku nie będzie można go uruchomić. Może się zdarzyć, że inne wątki również są powiadamiane (używają notifyAll()) lub pierwszy wątek nie zakończył swojej pracy, więc nadal jest blokowany, dopóki nie dostanie swojej szansy. Nazywa się to stanem zablokowanym . Stan Zablokowany wystąpi, gdy wątek spróbuje uzyskać blokadę obiektu, a inny wątek już utrzymuje blokadę.

Po opuszczeniu innych wątków i uzyskaniu tej szansy na wątek, przechodzi on do stanu Runnable, po czym kwalifikuje się do podjęcia pracy w oparciu o mechanizm wątków JVM i przechodzi do stanu uruchomienia.

Ankit Bansal
źródło
2
Wyjaśniłeś to znacznie lepiej, ponieważ wyjaśniłeś sekwencję, w której nić osiąga te dwa stany, co czyni ją bardziej zrozumiałą niż zwykłe wyjaśnienie każdego z dwóch stanów osobno (co jest zrobione przez odpowiedź "Więcej niż pięć"
Kumar Manish
7
Dla wszystkich, którzy zastanawiają się, dlaczego większość (wszystkich?) Diagramów stanu znalezionych w sieci twierdzi, że powiadomienie () / notifyAll () skutkuje RUNNABLE zamiast BLOCKED: stackoverflow.com/questions/28378592/ ...
Niklas Peter
Załóżmy, że jest tylko jeden wątek i czekałem przez pewien czas w milisach; teraz Czy jest możliwe, aby wątek mógł przejść bezpośrednio ze stanu oczekiwania do stanu gotowego do uruchomienia? ponieważ żaden inny wątek nie przyjmuje tu blokady, ponieważ tylko jeden wątek?
Kanagavelu Sugumar
Istnieje metoda wait (time), która powróci do stanu gotowego do pracy po upływie tego czasu. Ale jeśli nie zostanie określony czas, będzie czekał, aż inny wątek powiadomi lub zostanie przerwany.
Ankit Bansal
2
Twoja odpowiedź jest dobra, ale nie do końca wyjaśnia, że ​​możesz wejść w stan Zablokowany za każdym razem, gdy próbujesz zdobyć zamek. Nie ma to nic wspólnego z sygnałem / powiadomieniem.
Gray
90

Różnica jest stosunkowo prosta.

W tym BLOCKEDstanie wątek ma zamiar wejść do synchronizedbloku, ale obecnie w synchronizedbloku tego samego obiektu działa inny wątek . Pierwszy wątek musi następnie czekać, aż drugi wątek opuści swój blok.

W tym WAITINGstanie wątek oczekuje na sygnał z innego wątku. Dzieje się to zwykle przez telefon Object.wait()lub Thread.join(). Wątek pozostanie w tym stanie do czasu wywołania innego wątku Object.notify()lub śmierci.

Flavio
źródło
2
czy słuszne jest stwierdzenie, że tylko sama nić może sprawić, że zaczeka? Czy Thread-B może kiedykolwiek sprawić, że Thread-A przejdzie w stan WAIT?
Więcej niż pięć
1
Rzadko używasz Object.wait()bezpośrednio, ale kończysz w WAITINGstanie również przy użyciu konstrukcji współbieżności wyższego poziomu - takich jak blokady, kolejki blokujące itp., Mówiąc ogólnie, gdy dwa wątki muszą koordynować.
Flavio
1
Z własnego doświadczenia wynika, że ​​wątki oczekujące na IO (np. Odczyt z gniazda) są w RUNNINGstanie.
Flavio,
4
Dokument Java8 dla Thread.Statemówi: „... Te stany to stany maszyny wirtualnej, które nie odzwierciedlają żadnych stanów wątków systemu operacyjnego”. Innymi słowy, maszyna JVM nie dba o różnicę między wątkiem, w którym działa kod Java, wątkiem oczekującym na zwrot wywołania systemowego, a wątkiem oczekującym na wycinek czasu. To wszystko RUNNABLEdotyczy JVM.
Solomon Slow
3
Fajnie byłoby dodać, że kiedy wątek przechodzi ze WAITINGstanu, musi najpierw przejść do tego BLOCKEDstanu, aż będzie mógł uzyskać blokadę związaną z obiektem, na który czekał.
Gray
23

Ważną różnicą między stanami zablokowanymi i oczekującymi jest wpływ na harmonogram. Wątek w stanie zablokowanym walczy o blokadę; ten wątek nadal liczy się jako coś, co planista musi obsłużyć, prawdopodobnie biorąc pod uwagę decyzje planisty dotyczące tego, ile czasu ma dać działającym wątkom (aby mógł dać wątkom blokowanie blokady).

Gdy wątek znajduje się w stanie oczekiwania, obciążenie systemu jest zminimalizowane, a planista nie musi się tym martwić. Przechodzi w stan uśpienia, dopóki nie otrzyma powiadomienia. Z wyjątkiem faktu, że utrzymuje zajęty wątek systemu operacyjnego, jest całkowicie poza grą.

To dlatego użycie notifyAll jest mniej niż idealne, powoduje, że kilka wątków, które wcześniej były szczęśliwie uśpione, nie obciążały systemu, aby się obudzić, gdzie większość z nich będzie blokować, dopóki nie zdobędą blokady, znajdą stan, w którym są czekanie nie jest prawdą i wróć do czekania. Lepiej byłoby powiadamiać tylko te wątki, które mają szansę na postęp.

(Użycie ReentrantLock zamiast wewnętrznych blokad pozwala mieć wiele warunków dla jednej blokady, dzięki czemu można upewnić się, że powiadomiony wątek jest tym, który czeka na określony warunek, unikając błędu zgubienia powiadomienia w przypadku powiadomienia o wątku coś, na co nie może działać.)

Nathan Hughes
źródło
Czy dzieje się tak dlatego, że za wywołanie funkcji notify () na obiekcie monitora odpowiedzialne są inne wątki?
berimbolo
@berimbolo: Nie rozumiem, o co pytasz
Nathan Hughes
Chodziło o to, dlaczego oczekujący wątek nie jest czymś, o co planista powinien się martwić. Zastanawiałem się, czy to dlatego, że inny wątek będzie odpowiedzialny za wywołanie powiadomienia, jeśli czeka.
berimbolo
@berimbolo: oczekujący wątek zostanie ostatecznie obudzony przez powiadomienie. Planista zdecydowałby, który oczekujący wątek zostanie powiadomiony.
Nathan Hughes
coś liczy, mówisz blokada spinowa, dawka ZABLOKOWANA nie oznacza, że ​​blokada spinowa
Frank Zhang
16

Uproszczona perspektywa interpretacji zrzutów wątków:

  • CZEKAJ - czekam na pracę, więc jestem teraz bezczynny.
  • ZABLOKOWANE - Jestem zajęty wykonywaniem pracy, ale na drodze stoi mi inny wątek, więc jestem teraz bezczynny.
  • RUNNABLE ... (metoda rodzima ) - wezwałem URUCHOMIĆ kod natywny (który jeszcze się nie skończył), więc jeśli chodzi o JVM, jesteś RUNNABLE i nie może podać żadnych dalszych informacji. Typowym przykładem może być natywna metoda nasłuchiwania gniazd zakodowana w C, która faktycznie czeka na nadejście jakiegokolwiek ruchu, więc jestem teraz bezczynny. W tej sytuacji można to postrzegać jako specjalny rodzaj CZEKANIA, ponieważ w rzeczywistości wcale NIE URUCHAMIAMY (brak spalania procesora), ale aby to zobaczyć, trzeba użyć zrzutu wątku systemu operacyjnego zamiast zrzutu wątku Java.
stary facet
źródło
1
Podoba mi się twoje wyjaśnienie. Właśnie to próbuję teraz zrobić analizując zrzuty wątków :)
Sridhar Sarnobat
@MuhammadGelbana Tak, masz rację, usunąłem komentarz.
Eric Wang
1
Twój RUNNABLEnie jest całkiem w porządku. Może znajdować się w kolejce uruchomień języka Java, ale nie może być wykonywany lub może wykonywać kod Java. Nie musi to być wołanie do ojczyzny.
Gray
1

Zablokowany - Twój wątek znajduje się w stanie gotowości do uruchomienia i próbuje uzyskać blokadę obiektu. Czekaj - Twój wątek jest w stanie oczekiwania na cykl życia wątku i czeka na sygnał powiadamiający, aby przejść w stan gotowości wątku do uruchomienia.

Prakash Bisht
źródło
-1

zobacz ten przykład:

demonstracja stanów wątku.

/*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
źródło
Dziękuję za kod, ale wolałbym, żebyś miał odpowiedź tekstową, a następnie pokazał mały blok kodu.
Gray