一、五种线程状态
从操作系统层面来描述线程状态,共有初始状态、可运行状态、运行状态、阻塞状态、终止状态五种状态
- 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统的线程相关联,例如在Java中则为仅创建了Thread对象但是没调用start方法启动线程;
- 【可运行状态】/【就绪状态】指该线程可由cpu调度,随时可以运行,就差CPU分配时间片,其它条件完全不缺。注意只有【可运行状态】的线程才会被分配时间片,其余如【阻塞态】、【初始态】的线程不会被分配时间片;
- 【运行状态】指获取了CPU时间片的状态,即正在运行的状态;
【阻塞状态】
如果调用了阻塞API,如sleep方法、BIO读写文件,这时该线程实际不会用到CPU,会导致线程上下文切换,进入【阻塞状态】;<br /> 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】,注意阻塞态不会立刻转换到【运行状态】;
【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态;
二、六种线程状态
从Java API层面描述,根据Thread.State枚举,线程共分为六种状态,可以去源码的State枚举类查看所有线程状态
- 【NEW】,new线程刚被创建,跟操作系统层面的初始状态对应,即线程刚被创建,但是还没有调用start()方法;
- 【RUNNABLE】,注意Java API层面的RUNNABLE状态涵盖了操作系统层面的【可运行状态】、【运行状态】、【阻塞状态】(由于BIO导致的线程阻塞,在Java里无法区分,仍然认为是RUNNABLE状态)
- 【TERMINATED】,即线程代码运行结束后,线程进入终止状态;
- 【BLOCKED】,等待获取锁,一旦有其他线程释放这把锁,线程成功抢到该锁,线程状态就会从【BLOCKED】状态转为【RUNNABLE】状态;
- 【WAITING】,处于【WAINTING】状态的线程将会一直处于无限期的等待状态,需要等待其他线程唤醒;
【TIMED_WAITING】,限时等待状态,一旦等待时间超时,线程状态自动变为【RUNNABLE】;
对【RUNNABLE】状态详解:
从JVM角度看,JVM并不关心操作系统线程实际状态,从JVM看等待CPU使用权(操作系统线程状态为可运行状态)与等待I/O(操作系统线程状态处于阻塞状态)没有区别,都是在等待某种资源,所以都归入RUNNABLE状态。故实际上,从JVM角度看,【运行状态】、【可运行状态】、【阻塞状态】都被视为【RUNNABLE】状态,但Java对阻塞状态进行了细分,包括【BLOCKED】、【WAITING】、【TIMED_WAITING】三种状态代表阻塞。
测试,从JVM角度来讲,当线程调用阻塞API(I/O)时,线程的状态是RUNNABLE,但实际从操作系统看是阻塞态。
import java.util.Scanner;
public class Test4 {
public static void main(String[] args) throws InterruptedException {
Scanner in =new Scanner(System.in);
Thread t =new Thread(()->{
try{
String input = in.nextLine();
System.out.println(input);
}catch (Exception e){
e.printStackTrace();
}
},"输入输出");
t.start();//启动线程t
Thread.sleep(100);//确保线程run已经得到执行,需要有一个小间隔
System.out.println(t.getState());
}
}
但如果是进入sleep状态的线程,在Java中有另外的线程状态名称,【TIMED_WAITING】
import java.util.Scanner;
public class Test4 {
public static void main(String[] args) throws InterruptedException {
Scanner in =new Scanner(System.in);
Thread t =new Thread(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"输入输出");
t.start();//启动线程t
Thread.sleep(100);//确保线程run已经得到执行,需要有一个小间隔
System.out.println(t.getState());
}
}
所以总结:
- 当线程涉及到I/O操作、调操作系统的阻塞API接口时,操作系统角度看是阻塞态(仍有条件没完成,如读写操作没完成),但从JVM角度看是Runnable态;
- 当JVM调用Java相关方法如sleep()、wait()方法时,有自己独立的线程状态名称,这里要与上一点做出区分;
- Java内部的线程有六种状态,是为了内部更容易书写代码,实际上仍要暴露给操作系统一个接口,让操作系统区分开线程到底实际是阻塞态还是就绪态,这样操作系统在进行CPU时间片分配,才不会混乱,JVM只是封装了一下。试想如果操作系统不能分清楚线程实际状态,那么整个操作系统就会陷入紊乱,比如操作系统按照JVM的规则通过调度器将【RUNNABLE】状态的线程放入就绪队列中,但【RUNNABLE】状态的线程有可能是阻塞态,阻塞态线程进入就绪队列显然是不合理的;
三、六种线程状态演示
import java.util.Scanner;
public class Test4 {
public static void main(String[] args) throws InterruptedException {
Thread t1= new Thread("t1"){
@Override
public void run() {
System.out.println("running...");
}
};
Thread t2= new Thread("t2"){
@Override
public void run() {
while(true){//runnable,有可能是正常运行,也有可能是陷入操作系统的阻塞状态中,会打印runnable
}
}
};
t2.start();
Thread t3= new Thread("t3"){
@Override
public void run() {
System.out.println("running...");
}
};
t3.start();
Thread t4= new Thread("t4"){
@Override
public void run() {
synchronized (Test4.class){
try {
Thread.sleep(100000);//t4属于timed_waiting状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
Thread t5= new Thread("t5"){
@Override
public void run() {
try {
t2.join();//与线程t2同步,一直在等待,所以线程是waiting状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
Thread t6= new Thread("t6"){
@Override
public void run() {
synchronized (Test4.class){//synchronized是锁的关键字,之前t4线程对Test4.class对象上锁,那么t6线程即拿不到该锁,陷入blocked状态
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
t2.join();//与线程t2同步,一直在等待,所以线程是waiting状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t6.start();
Thread.sleep(500);
System.out.println("t1 state:"+t1.getState());
System.out.println("t2 state:"+t2.getState());
System.out.println("t3 state:"+t3.getState());
System.out.println("t4 state:"+t4.getState());
System.out.println("t5 state:"+t5.getState());
System.out.println("t6 state:"+t6.getState());
}
}
输出结果:
可以看到线程未执行start方法则是【NEW】状态;当线程执行时的状态是【RUNNABLE】;线程执行完毕后的状态是【TERMINATED】;当线程调用Thread.sleep()方法后陷入【TIMED_WAITING】状态;某一线程调用join方法后,会与线程同步,即会一直等待线程执行完毕当前线程才会执行,此时线程陷入【WAITING】状态;当线程需要某个锁却拿不到时,则该线程会陷入【BLOCKED】状态。