线程状态概述和等待唤醒案例分析与实现
定义:在Java中,任何对象都有生命周期,线程也不例外,它也有自己的生命周期。对象创建完成时,线程的生命周期便开始了执行完毕或者线程抛出一个未捕获的异常(Exception)或者错误(Error)时,线程的生命周期便会结束。
线程转换图:
1.NEW(新建状态):创建一个线程对象后,该线程对象就处于新建状态,此时它不能运行,和其他Java对象一样,仅仅由JVM为其分配了内存,没有表现出任何线程的动态特征。
2.RUNNABLE(可运行状态):新建状态的线程调用start()方法,就会进入可运行状态。在RUNNABLE状态内部又可细分成两种状态:READY(就绪状态)和RUNNING(运行状态),并且线程可以在这两个状态之间相互转换。
RUNNABLE内部状态转换:
就绪状态:线程对象调用start()方法之后,等待JVM的调度,此时线程并没有运行;
运行状态:线程对象获得JVM调度,如果存在多个CPU,那么允许多个线程并行运行。
3.BLOCKED(阻塞状态):运行状态的线程因为某些原因失去CPU的执行权,会进入阻塞状态。阻塞状态的线程只能先进入就绪状态,不能直接进入运行状态。
线程进入阻塞状态的两种情况:
当线程A运行过程中,试图获取同步锁时,却被线程B获取;
当线程运行过程中,发出IO请求时。
4.WAITING(等待状态):
运行状态的线程调用了无时间参数限制的方法后,如wait()、join()等方法,就会转换为等待状态。
等待状态中的线程不能立即争夺CPU使用权,必须等待其他线程执行特定的操作后,才有机会争夺CPU使用权。
例如,调用wait()方法而处于等待状态中的线程,必须等待其他线程调用notify()或者notifyAll()方法唤醒当前等待中的线程;
调用join()方法而处于等待状态中的线程,必须等待其他加入的线程终止
5.TIMED_WAITING(定时等待状态):
运行状态中的线程调用了有时间参数限制的方法,如sleep(long millis)、wait(long timeout)、join(long millis)等方法,就会转换为定时等待状态。
定时等待状态中的线程不能立即争夺CPU使用权,必须等待其他相关线程执行完特定的操作或者限时 时间结束后,才有机会再次争夺CPU使用权。
例如,调用了wait(long timeout) 方法而处于等待状态中的线程,需要通过其他线程调用notify()或者notifyAll()方法唤醒当前等待中的线程,或者等待 限时 时间结束后也可以进行状态转换。
6.TERMINATED(终止状态):
线程的run()方法、call()方法正常执行完毕或者线程抛出一个未捕获的异常(Exception)、错误(Error),线程就进入终止状态。
一旦进入终止状态,线程将不再拥有运行的资格,也不能再转换到其他状态,生命周期结束。
等待唤醒案例分析与实现
package com.yang.Test.ThreadStudy;
import lombok.SneakyThrows;
/**
* 等待唤醒案例:线程之间通信
* 创建一个顾客线程(消费者):告知拉板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入WAIYING状态(无限等待)
* 创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子
* 注意:
* 顾客和包含线程必须使用同步代码块包裹起来,保证等到和唤醒只能有一个在执行
* 同步使用的锁对象必须保证唯一
* 只有锁对象才能调用wait和notify方法
*
* Object类中的方法
* void wait();在其他线程调用此对象的notify()方法或者notifyAll()方法前,导致当前线程等待。
* void notify();唤醒在此对象监视器上等待的单个线程会继续执行wait方法之后的代码
*/
public class WaitAndNotify {
public static void main(String[] args) {
//创建锁对象保证唯一
Object obj = new Object();
//创建一个顾客线程(消费者)
new Thread(){
@SneakyThrows
@Override
public void run() {
//一直等着吃包子
while(true){
//保证等到和唤醒只能有一个在执行
synchronized (obj){
System.out.println("告知拉板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入WAIYING状态(无限等待)
obj.wait();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,开吃!");
}
}
}.start();
//创建一个老板线程(生产者)
new Thread(){
@SneakyThrows
@Override
public void run() {
//一直做
while(true){
//花了5秒做包子
Thread.sleep(5000);
//保证等到和唤醒只能有一个在执行
synchronized (obj){
System.out.println("老板5秒钟之后做好包子,告知顾客可以吃包子了");
//做好包子之后,调用notify方法,唤醒顾客吃包子
obj.notify();
}
}
}
}.start();
}
}