WAIT 和阻塞线程状态之间的区别

线程状态 WAIT 和线程状态 BLOCKED 的区别是什么?

返回文章页面 线程,国家文件:

被屏蔽了
等待监视器锁定时阻塞的线程处于此状态。

等待
无限期地等待另一个线程执行特定操作的线程处于这种状态

不能解释我们之间的区别。

94015 次浏览

区别相对简单。

BLOCKED状态下,一个线程即将进入 synchronized块,但是当前在同一对象的 synchronized块内运行着另一个线程。然后,第一个线程必须等待第二个线程退出其块。

WAITING状态下,一个线程正在等待来自另一个线程的信号。这通常是通过调用 Object.wait()Thread.join()来实现的。然后,该线程将保持这种状态,直到另一个线程调用 Object.notify()或死亡。

一旦线程对 Object 调用 wait(),它就进入等待状态。这就是所谓的 等待状态。一旦线程达到等待状态,它将需要等待,直到其他线程调用对象上的 notify()notifyAll()

一旦通知此线程,它将不能运行。可能还会通知其他线程(使用 notifyAll()) ,或者第一个线程尚未完成其工作,因此它仍然被阻塞,直到获得机会。这就是所谓的 被屏蔽了状态。每当一个线程试图获取对象上的锁,而其他线程已经持有该锁时,就会出现阻塞状态。

一旦其他线程离开,并且有这个线程的机会,它就会转移到 Runnable 状态,然后它就有资格基于 JVM 线程机制拾取工作,并转移到运行状态。

阻塞-线程处于线程生命周期的可运行状态,并尝试获取对象锁。 等待-您的线程处于线程生命周期的等待状态,并等待通知信号进入线程的可运行状态。

解释线程转储的简化视角:

  • 我正在等待一些工作,所以我现在闲着。
  • BLOCKED -我正忙着完成工作,但另一个线程挡住了我的去路,所以我现在无所事事。
  • RUNNABLE ... (本机方法)——我调用运行一些本机代码(还没有完成) ,所以就 JVM 而言,您是 RUNNABLE,它不能提供任何进一步的信息。一个常见的例子是用 C 编写的本机套接字侦听器方法,它实际上在等待任何流量到达,所以我现在处于空闲状态。在这种情况下,这可以被看作是一种特殊类型的 WAIT,因为我们实际上根本没有运行(没有 CPU 消耗) ,但是您必须使用 OS 线程转储而不是 Java 线程转储才能看到它。

看这个例子:

线程状态的演示。

/*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();
}
}
}
}

阻塞状态和等待状态之间的重要区别是对调度程序的影响。处于阻塞状态的线程正在争夺一个锁; 这个线程仍然算作调度程序需要服务的东西,可能被计入调度程序关于给运行线程多少时间的决定中(这样它就可以给锁上阻塞的线程一个机会)。

一旦线程处于等待状态,它给系统带来的压力就会最小化,调度程序就不必担心这个问题了。在收到通知之前,它一直处于休眠状态。除了事实上,它保持一个操作系统线程占用它是完全出局。

这就是为什么使用 notifyAll 不够理想的原因,它会导致一大堆之前愉快休眠的线程在系统上没有加载就被唤醒,其中大多数线程将会阻塞,直到它们能够获得锁,发现它们等待的条件不是真的,然后回到等待状态。最好只通知那些有机会取得进展的线程。

(使用 ReentrantLock 代替内部锁可以让你为一个锁设置多个条件,这样你就可以确保通知的线程是一个正在等待特定条件的线程,避免线程因为无法执行某些操作而被通知丢失通知的 bug