Разница между состояниями WAIT и BLOCKED потока

102

В чем разница между состоянием потока WAIT и состоянием потока ЗАБЛОКИРОВАНО?

Документация Thread.State :

Заблокирован.
В этом состоянии находится поток, который заблокирован в ожидании блокировки монитора.

Ожидание
Поток, который бесконечно ожидает выполнения другим потоком определенного действия, находится в этом состоянии.

не объясняет мне разницу.

Более пяти
источник
проверьте ответ в этой теме stackoverflow.com/questions/2534147/java-thread-wait-blocked, также эта ссылка может предоставить дополнительные разъяснения geekexplains.blogspot.cz/2008/07/…
Абдул
@Abdul ссылка geekexplains говорит, что поток может перейти в заблокированное состояние, вызвав Object.wait (), это неправильно, не так ли?
More Than Five
согласно документам oracle docs.oracle.com/javase/6/docs/api/java/lang/… : поток находится в состоянии ожидания из-за вызова одного из следующих методов: Object.wait без тайм-аута, Thread.join без тайм-аута, LockSupport.park
Абдул
Для записи, я думаю, что ответ @ Flavio немного лучше, чем у Ankit, на случай, если вы захотите изменить.
Gray

Ответы:

80

После вызова wait()объекта поток переходит в состояние ожидания . Это называется состоянием ожидания . Когда поток достигает состояния ожидания, ему нужно будет дождаться вызова какого-либо другого потока notify()или вызова notifyAll()объекта.

Как только этот поток будет уведомлен, его нельзя будет запустить. Возможно, другие потоки также уведомляются (используют notifyAll()) или первый поток не завершил свою работу, поэтому он все еще заблокирован, пока не получит свой шанс. Это называется заблокированным состоянием. Состояние «Заблокировано» возникает всякий раз, когда поток пытается получить блокировку объекта, а какой-либо другой поток уже удерживает блокировку.

Как только другие потоки уйдут и у него появится шанс этого потока, он переходит в состояние Runnable, после чего он имеет право выполнять работу на основе механизма потоковой передачи JVM и переходит в состояние выполнения.

Анкит Бансал
источник
2
Вы объяснили это намного лучше, потому что вы объяснили последовательность, в которой поток достигает этих двух состояний, что делает его более понятным, чем простое объяснение каждого из двух состояний по отдельности (что делается с помощью ответа «Более пяти»
Кумар Маниш,
7
Для всех, кто задается вопросом, почему большинство (все?) Диаграмм состояний, найденных в веб-заявлении, что notify () / notifyAll () приводит к RUNNABLE вместо BLOCKED: stackoverflow.com/questions/28378592/…
Никлас Питер
Предположим, что есть только один поток и он ждал некоторое время в миллисекундах; Теперь возможно ли, что поток может прямо из состояния ожидания перейти в рабочее состояние? поскольку никакой другой поток не принимает здесь блокировку, поскольку только однопоточный?
Kanagavelu Sugumar
Существует метод ожидания (времени), который вернется в рабочее состояние по истечении времени. Но если время не указано, он будет ждать, пока другой поток не уведомит об этом или пока поток не будет прерван.
Ankit Bansal
2
Ваш ответ хорош, но он не совсем объясняет, что вы можете войти в состояние «Заблокировано» в любое время, когда пытаетесь получить блокировку. Это не должно иметь ничего общего с сигналом / уведомлением.
Gray
90

Разница относительно проста.

В этом BLOCKEDсостоянии поток собирается войти в synchronizedблок, но в данный момент внутри synchronizedблока на том же объекте выполняется другой поток . Затем первый поток должен дождаться выхода второго потока из своего блока.

В этом WAITINGсостоянии поток ожидает сигнала от другого потока. Обычно это происходит при вызове Object.wait(), или Thread.join(). Затем поток будет оставаться в этом состоянии до тех пор, пока другой поток не вызовет Object.notify()или не умрет.

Флавио
источник
2
правильно ли говорить, что только поток может заставить его ждать? Может ли Thread-B когда-либо заставить Thread-A перейти в состояние WAIT?
More Than Five
1
Вы редко используете Object.wait()напрямую, но в конечном итоге в WAITINGсостоянии также использовать более высокоуровневые конструкции параллелизма - такие как блокировки, блокирующие очереди и т. Д. В общем, когда два потока должны координировать свои действия.
Flavio
1
По личному опыту, потоки, ожидающие ввода-вывода (например, чтения из сокета), находятся в RUNNINGсостоянии.
Флавио
4
В документе Java8 Thread.Stateговорится: «... Эти состояния представляют собой состояния виртуальных машин, которые не отражают состояния потоков операционной системы». Другими словами, JVM не заботится о разнице между потоком, выполняющим код Java, потоком, ожидающим возврата системного вызова, или потоком, ожидающим временного отрезка. Все это RUNNABLEкасается JVM.
Соломон Слоу
3
Было бы неплохо добавить, что когда поток выходит из WAITINGсостояния, он должен сначала перейти в это BLOCKEDсостояние, пока не сможет получить блокировку, связанную с объектом, которого он ожидал.
Gray
23

Важное различие между заблокированным состоянием и состоянием ожидания - влияние на планировщик. Поток в заблокированном состоянии борется за блокировку; этот поток по-прежнему считается чем-то, что планировщик должен обслуживать, и, возможно, учитывается в решениях планировщика о том, сколько времени дать запущенным потокам (чтобы он мог дать шанс потокам, блокирующим блокировку).

Когда поток находится в состоянии ожидания, нагрузка, которую он оказывает на систему, сводится к минимуму, и планировщику не нужно об этом беспокоиться. Он бездействует, пока не получит уведомление. За исключением того факта, что он сохраняет занятость потока ОС, он полностью не работает.

Вот почему использование notifyAll далеко не идеально, оно приводит к тому, что куча потоков, которые ранее благополучно бездействовали, не загружая систему, просыпается, где большинство из них будут блокироваться, пока не получат блокировку, найдут состояние, в котором они находятся. ожидание не соответствует действительности, и возвращайтесь к ожиданию. Было бы предпочтительнее уведомлять только те потоки, у которых есть шанс добиться прогресса.

(Использование ReentrantLock вместо внутренних блокировок позволяет вам иметь несколько условий для одной блокировки, так что вы можете убедиться, что уведомляемый поток - это тот, который ожидает определенного условия, избегая ошибки с потерянным уведомлением в случае, если поток получает уведомление для то, на что он не может воздействовать.)

Натан Хьюз
источник
Это потому, что вызов notify () объекта монитора - это ответственность других потоков?
беримболо 08
@berimbolo: Я не понимаю, о чем вы спрашиваете,
Натан Хьюз
Это было связано с тем, почему планировщик не должен беспокоиться об ожидающем потоке. Я задавался вопросом, было ли это потому, что другой поток будет отвечать за вызов уведомления, если он ожидает.
беримболо 09
@berimbolo: ожидающий поток в конечном итоге будет разбужен уведомлением. Планировщик решит, какой ожидающий поток получит уведомление.
Натан Хьюз
что-то считает, вы говорите, что спин-блокировка, ЗАБЛОКИРОВАННАЯ доза не означает, что это спин-блокировка
Фрэнк Чжан
16

Упрощенная перспектива для интерпретации дампов потоков:

  • ПОДОЖДИТЕ - я жду, чтобы мне дали работу, так что я сейчас без дела.
  • ЗАБЛОКИРОВАНО - я занят, пытаясь выполнить работу, но на моем пути стоит другой поток, поэтому сейчас я не работаю.
  • ВЫПОЛНЯЕМЫЙ ... (собственный метод) - я призвал ЗАПУСТИТЬ некоторый собственный код (который еще не завершен), так что, что касается JVM, вы ЗАПУСКАЕТСЯ, и он не может предоставить никакой дополнительной информации. Типичным примером может быть собственный метод прослушивателя сокетов, закодированный на C, который на самом деле ожидает прибытия любого трафика, поэтому сейчас я простаиваю. В этой ситуации это можно рассматривать как особый вид WAIT, поскольку на самом деле мы вообще не ЗАПУСКАЕМ (не сжигаем ЦП), но вам придется использовать дамп потока ОС, а не дамп потока Java, чтобы увидеть его.
старик
источник
1
Мне нравится ваше объяснение. Это именно то, что я пытаюсь сделать прямо сейчас при анализе дампов потоков :)
Шридхар Сарнобат 01
@MuhammadGelbana Да, вы правы, я удалил комментарий.
Эрик Ван
1
Вы RUNNABLEне совсем правы. Он может находиться в очереди выполнения Java, но не выполняться, или может выполнять код Java. Это не должно быть призывом к родной земле.
Gray
1

Заблокирован - ваш поток находится в рабочем состоянии жизненного цикла потока и пытается получить блокировку объекта. Ждите. Ваш поток находится в состоянии ожидания жизненного цикла потока и ожидает сигнала уведомления, чтобы перейти в рабочее состояние потока.

Пракаш Бишт
источник
-1

см. этот пример:

демонстрация состояний потоков.

/*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();
        }
    }
}
}
мурали
источник
Спасибо за код, но я бы предпочел, чтобы вы получили текстовый ответ, а затем показали небольшой блок кода.
Gray