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

В чем разница между состоянием потока WAIT и состоянием потока BLOCKED?

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

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

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

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

6 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

  • ПОДОЖДИТЕ - Я жду, когда мне дадут какую-то работу, поэтому я сейчас бездействую.
  • БЛОКИРОВАН - я занят, пытаясь закончить работу, но у меня на пути стоит другая нить, так что я сейчас без дела.
  • RUNNABLE... (собственный метод) - я вызвал для запуска некоторый нативный код (который еще не закончен), так что в отношении JVM вы работаете и он не может дать никакой дополнительной информации. Типичным примером может служить собственный метод прослушивания сокетов, закодированный в C, который фактически ожидает поступления любого трафика, поэтому я сейчас бездействую. В этой ситуации это можно рассматривать как особый вид WAIT, так как на самом деле мы вообще НЕ РАБОТАЕМ (не сжигаем процессор), но вам нужно использовать дамп потока ОС, а не дамп потока Java, чтобы увидеть его.

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

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

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

/*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();
        }
    }
}
}
Другие вопросы по тегам