首先要明确一点的是,java线程状态是指虚拟机层面暴露给我们的状态,操作系统(windows,linux等)的线程状态可能是N种,与java并不是一一对应 的。但不管是N种还是M种都与java的六种所对应。为什么有时候java线程的优先级感觉不出来有什么效果,这也是因为java线程的优先级与操作系统线程的优先级并不是一一划等号的。
处于阻塞状态 (B locked ) 的线程,并不会占用CPU资源。但频繁的挂起与唤醒是很耗费CPU 资源的。 sleep方法 与 wait方法 都会释放此时占用的CPU资源。 wait方法 / sleep方法 皆响应中断方法且同为 native方法 。 sleep方法 必须有参数。 wait方法 有两种一种有参一种无参。 sleep方法 无必须 在同步方法或同步块儿中所执行, wait方法 需要在同步块儿中使用。调用 sleep方法的线程 不会失去任何monitor对象的所有权 ,相反调用 wait方法 的线程会释放锁资源。 wait方法 是实例方法定义在Object类中,作用于对象本身。 sleep方法 定义在Thread类中,作用于当前线程。唤醒的方式也不同,被 wait方法 阻塞的线程需要其他线程调用对象的 notify()/notifyAll()唤醒 或者超时或中断 interrupt() 方法来唤醒。“ 睡眠的线程 ”的线程要么超时被唤醒,要么中断 interrupt() 方法来唤醒。 notify()/notifyAll()/ wait() 配合使用。如果线程正等待未满足条件触发时即可 wait() ,待条件为真则调用 notify() & notifyAll() 方法唤醒那些线程重新开始,这些线程的状态会置为Runable。 notify() 方法是从等待中的线程中随机取一个去唤醒,而 notifyAll() 则是唤醒全部。 如何在代码中使用wait() 方法? 答:正确的方法是在多线程共享的那个Object来使用wait() 方法。在生产者消费者问题中,这个共享的Object就是那个缓冲区队列。对应的如果是同步静态方法,其wait的对象应该是当前对象.class.wait(); 既然我们应该在synchronized的函数对象里调用wait() ,那么哪个对象应该被synchronized呢? 答:希望上锁的对象就应该被synchronized。那个在多线程共享的对象。在生产者消费者问题中,应该被synchronized的就是那个缓冲队列。
// The standard idiom for calling the wait method in Java
synchronized (sharedObject) {
while (condition) {
sharedObject.wait();
// (Releases lock, and reacquires on wakeup)
}
// do action based upon condition e.g. take or put into queue
}
notify()/notifyAll()/ wait() 方法都需要在同步块儿中才能使用。wait() 方法的常见使用场景就是在while循环体判断 条件是否满足,如果不满足,那么就阻塞线程。不能用if,如果使用if的话 ,条件就被一次性的判断错过去了。循环要写在方法体的开始位置 ,避免因条件没有满足而污染业务逻辑。业界用语 - 虚假唤醒 想明白了一点,为什么要通过while循环+wait()去控制程序。是避免CPU的资源浪费,如果你没有获取资源的条件 你就回去“睡会儿”,别占用CPU了。 NotifyAll与Notify方法虽说是主观功能相似,实则还有些许区别。NotifyAll会引起“惊群效应 ”。一下子唤醒众多线程,此时的线程们皆由状态WAITTING变为RUNABLE,然而只会有一个线程最终获得执行权限,那么其他未获得执行时间的线程变“索然无味的”再次回到Wait状态。频繁挂起唤醒。 惊群效应 (thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应 。依据上一条,那么什么时候用NotifyAll及Notify呢。我感觉CPU资源不是很紧张的时候,可以使用NotifyAll,资源紧张时用Notify。从整个程序的运转角度说,一次唤醒全部最终一个执行再把其他阻塞,与一次唤醒一个相比区别还是挺大的。