多线程——线程状态
目录
1.线程的状态
1.1 NEW
1.2 RUNNABLE
1.3 BLOCKED
1.4 WAITING
1.5 TIMED_WAITING
1.6 TERMINATED
2.线程状态的相互转换
在上期的学习中,已经理解线程的启动(start()
)、休眠(sleep()
)、中断(interrupt()
)和等待(join()
)等核心概念。通过这些操作,线程会在不同的状态之间切换。那么,线程在这些过程中究竟经历了哪些状态?它们是如何转换的?这是本期要探讨的问题。
在前面谈到进程状态时,主要讲了就绪状态和阻塞状态,这是两个经典的状态。在线程中,线程的状态有六种,分别是:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。这几个状态在 Java 中属于枚举类型,可以用代码实现查看这些状态:
public class Demo14 {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}
下面将对它们一 一介绍。
1.线程的状态
1.1 NEW
小帅对自己的女神小美仰慕已久,小帅同学打算好好打扮自己去追求女神小美,但是小帅心里胆怯,所以到目前为止,只是停留在仰慕的状态。此时小帅心里有追求女神小美的想法,但还没开始行动 start(),属于 NEW 状态。
NEW:是指一个线程已经被创建(new),但是还没开始执行的状态,称新建状态,也就是这个时候还没用调用 start()。
public class Demo14 {public static void main(String[] args) {Thread thread = new Thread(() -> {//.........},"小帅");System.out.println(thread.getName() + ":" + thread.getState());//thread.start();//这里已经注释,没有调用start()}
}
运行结果:
1.2 RUNNABLE
终于,小帅下定决心,开启了对小美的追求之路。每天会和女神聊聊天,偶尔会约女神吃饭,也会给女神送礼物。此时小帅已经有实际行动了 start(),女神小美可能会立即回复,也有可能在忙其他事情,属于 RUNNABLE 状态。
RUNNABLE:是指线程正在 JVM 中正在执行或者准备执行的状态,成可运行状态。也就是线程已经创建,并且调用了 start() 方法,新建线程开始执行。
public class Demo14 {public static void main(String[] args) {Thread thread = new Thread(() -> {},"小帅");System.out.println("调用start()前:" + thread.getName() + ":" + thread.getState());thread.start();System.out.println("调用start()后:" + thread.getName() + ":" + thread.getState());}
}
运行结果:
1.3 BLOCKED
小帅追求一段时间后,小美也对他有所回应,于是小帅想约小美周末一起看个电影,但是小美说你约晚了,周末要和好闺蜜一起去逛街。此时小帅不能约到小美,因为小美被闺蜜占用了,属于BLOCKED 状态。
BLOCKED:是指线程处于阻塞状态。这个是由于锁导致的,在下期线程安全问题会有讲解,这里先做一个了解。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker){while (true) {System.out.println("闺蜜和小美在逛街....");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}},"闺蜜");Thread thread2 = new Thread(() -> {synchronized (locker){System.out.println("小帅约小美看电影....");}},"小帅");thread1.start();thread2.start();}
}
这里需要借助 jconsole 查看状态,关于 jconsole 在前面的文章《多线程——认识Thread类和创建线程》有讲。状态如下:
可以看到,“小帅”这个线程的状态就是 BLOCKED。
1.4 WAITING
时间来到第二周,虽然上周小帅没有约到小美一起看电影,但是啊,小帅没有放弃,于是小帅再次鼓气勇气再约女神一次,这次女神说这周末目前没有安排,但是这周工作有点忙,具体要看后面的时间安排,于是说“等我有时间联系你把”,但是小美并没有说会多久联系。于是呢,小帅觉得有点希望的,所以就一直等啊等,不分白天黑夜的一直在等女神的消息。此时,小帅一直等,没有期限的等待就属于 WAITING 状态。
WAITING:是指某线程无限期等待其他线程执行特定任务的状态,称等待状态。也就是说需要其他线程完成一定的任务后,这个状态才能进行,需要调用 join() 方法。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {while (true) {try {System.out.println("等待小美联系中....");Thread.currentThread().join();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"小帅");Thread thread2 = new Thread(() -> {System.out.println("回复了小帅的邀约....");},"小美");thread1.start();thread1.join();thread2.start();}
}
借助 jconsole 查看状态,状态如下:
1.5 TIMED_WAITING
如果上面例子中,如果小美对小帅说“我这周比较忙,不一定有时间,我两三天内再回复你能不能出去看电影吧。”此时,小帅就会等小美的回复,但不会一直等下去了,最多就等三天,此时的等待就属于 TIMED_WAITING。
TIMED_WAITING:是指线程在指定的时间内等待,这个等待有期限,称计时等待状态。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {while (true) {System.out.println("等待小美联系中....");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"小帅");Thread thread2 = new Thread(() -> {System.out.println("回复了小帅的邀约....");},"小美");thread1.start();thread1.join(5000);//这里设置等待5秒thread2.start();}
}
借助 jconsole 查看状态,状态如下:
1.6 TERMINATED
小帅和小美在联系一段时间后,小帅和小美决定彼此并不合适,所以并没有在一起,小帅也放弃了追求小美,至此小帅对仰慕已久的女神的追求彻底结束了,属于 TERMINATED 状态。
TERMINATED:是指线程执行完毕的状态,称终止状态。
public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{//....},"小帅");thread.start();thread.join();System.out.println(thread.getName() + "对女神的追求结束了... 现在的状态是:" + thread.getState());}
}
运行结果:
2.线程状态的相互转换
虽然线程状态的相互转换看似复杂,但主要是理解各个状态的含义以及是怎么发生的,这对于在后续的多线程学习中是非常重要的,尤其是调试程序找 bug 的时候,想要在多线程程序中进行调试,理解线程的状态是非常重要的,并且还需会使用 jconsole 或者其他根据查看当前线程的状态,比如前文使用 jconsole 查看状态时,还可以看到一些其他信息比如当前的状态是发生在哪一行。在这张转换图里,看到了一些方法比如 wait(),这个也是等待的方法,用法和 join() 类似,但在这里不深入讲解,在后面会专门写一期 wait() 的用法,以及 synchronized() { } 等都会详细解析介绍到。
本期主要介绍多线程的线程状态,主要过程状态是NEW -> RUNNABLE -> TERMINATED,然后在这个过程中发生一些状态转换,主要理解每个状态的含义并可以借助一些工具如 jconsole 查看线程状态。
从多线程的创建学习到现在,我们也理解了多线程的优势,但是那多线程一定很安全吗?是不是多线程可以随意使用呢?在 Java 基础学习中,我们谈到 StringBuffer 和 StringBuilder 时,说 StringBuffer 是线程安全的,而 StringBuilder 是线程不安全的。可见线程会存在安全问题,为什么会有线程安全问题以及导致线程安全问题的原因是什么?
欲知后事如何,且听下回分解!