💗推荐阅读文章💗
- 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》
- 🌺MySQL系列🌺👉2️⃣《MySQL系列教程》
- 🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》
- 🌻SSM框架系列🌻👉4️⃣《SSM框架系列教程》
🎉本博客知识点收录于🎉👉🚀《JavaSE系列教程》🚀—>✈️13【线程等待、状态、线程池、File类】✈️
文章目录
- 二、线程状态
- 2.1 线程状态
- 2.1.1 NEW
- 2.1.2 Runnable
- 2.1.3 Blocked
- 2.1.4 Timed Waiting
- 2.1.5 Waiting
- 2.1.6 Terminated
- 2.2 线程状态变化
- 2.2.1 线程状态的转变
- 1)Runnable与Blocked状态转换
- 2)Runnable与Waiting状态转换
- 3)Runnable与Timed Waiting状态转换
- 2.2.2 状态变化的注意事项
二、线程状态
2.1 线程状态
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State
这个枚举中给出了六种线程状态:
这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析;
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。 |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
2.1.1 NEW
New(新建):表示线程被创建但尚未启动的状态:当我们用 new Thread() 新建一个线程时,如果线程没有开始运行 start() 方法,那么线程也就没有开始执行 run() 方法里面的代码,那么此时它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable
- 示例代码:
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_新建状态 {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
}
};
// 线程未启动之前的状态都是NEW
System.out.println(t1.getState()); // NEW
}
}
2.1.2 Runnable
Runnable被称为可运行状态,一旦线程调用start()方法,线程就处于可运行状态(Runnable)。
一个可运行的线程能正在运行也可能没有运行。有些教科书上讲可运行状态分为了就绪状态和运行状态,即线程开启后进入就绪状态,当线程抢到CPU执行权后进入运行状态(Java规范没有将正在运行作为一个单独的状态,一个正在运行的线程仍然处于可运行状态)
- 示例代码:
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_线程状态_RUNNABLE{
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
while (true){
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
while (true){
}
}
};
t1.start();
t2.start();
// 线程只要开启之后不管是否处于运行状态
System.out.println(t1.getState()); // RUNNABLE
System.out.println(t2.getState()); // RUNNABLE
}
}
2.1.3 Blocked
从 Runnable 状态进入到 Blocked 状态只有一种途径,那么就是当进入到 synchronized 代码块中时未能获得相应的锁,当有线程从 Blocked 状态指向了 Runnable ,也只有一种情况,那么就是当线程获得锁,此时线程就会进入 Runnable 状体中参与 CPU 资源的抢夺
【示例代码】
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_线程状态_RUNNABLE {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
synchronized (Object.class) {
while (true) {}
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
// t2线程获取不到锁,被锁阻塞在外面
synchronized (Object.class) {
}
}
};
t1.start();
// 过一会再开启线程2(确保线程1必定先获取CPU执行权)
Thread.sleep(100);
t2.start();
System.out.println(t1.getState()); // RUNNABLE
System.out.println(t2.getState()); // BLOCKED
}
}
2.1.4 Timed Waiting
Timed Waiting 状态,它与 Waiting 状态非常相似,其中的区别只在于是否有时间的限制,在 Timed Waiting 状态时会等待超时,之后由系统唤醒
在以下情况会让线程进入 Timed Waiting 状态。
- 线程执行了设置了时间参数的 Thread.sleep(long millis) 方法;
- 线程执行了设置了时间参数的 Object.wait(long timeout) 方法;
- 线程执行了设置了时间参数的 Thread.join(long millis) 方法;
通过这个我们可以进一步看到它与 waiting 状态的相同
【示例代码1】:
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_线程状态_TIMED_WAITING {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
// 让main线程睡眠一段时间(确保t1线程能够执行)
Thread.sleep(100);
System.out.println(t1.getState()); // TIMED_WAITING
}
}
【示例代码2】
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05_线程状态_TIMED_WAITING_02 {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
synchronized (Object.class){
while (true){
try {
Object.class.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
t1.start();
// 让main线程睡眠一段时间(确保t1线程能够执行)
Thread.sleep(100);
System.out.println(t1.getState()); // TIMED_WAITING
}
}
【示例代码3】
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo06_线程状态_TIMED_WAITING_03 {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while (true) {
try {
// 让t1线程先执行10s,t2将处于TIMED_WAITING状态
t1.join(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
// 让main线程睡眠0.1s,保证t1线程能执行join方法
Thread.sleep(100);
System.out.println(t1.getState()); // RUNNABLE
System.out.println(t2.getState()); // TIMED_WAITING
}
}
2.1.5 Waiting
对于 Waiting 状态的进入有2种情况,分别为:
- 1)当线程中调用了没有设置 Timeout 参数的 Object.wait() 方法
- 2)当线程调用了没有设置 Timeout 参数的 Thread.join() 方法
- 3)当线程调用了 LockSupport.park() 方法
【示例代码1】:
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo07_线程状态_WAITING {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
synchronized (Object.class) {
try {
Object.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
// 让当前线程睡眠0.1s,确保t1线程能够执行wait
Thread.sleep(100);
System.out.println(t1.getState()); // WAITING
}
}
【示例代码2】:
package com.dfbz.demo;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo08_线程状态_WAITING_02 {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while (true){
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
t2.start();
// 让当前线程睡眠0.1s,确保t1线程能够执行join
Thread.sleep(100);
System.out.println(t1.getState()); // RUNNABLE
System.out.println(t2.getState()); // WAITING
}
}
【示例代码3】:
package com.dfbz.demo;
import java.util.concurrent.locks.LockSupport;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo09_线程状态_WAITING_03 {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while (true){
LockSupport.park();
}
}
};
t1.start();
// 让当前线程睡眠0.1s,确保t1线程能够执行park
Thread.sleep(100);
System.out.println(t1.getState()); // WAITING
}
}
Blocked与Waiting的区别:
- Blocked 是在等待其他线程释放锁
- Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。
2.1.6 Terminated
Terminated:终止状态,要想进入Terminated 状态有两种可能。
- 1)run() 方法执行完毕,线程正常退出。
- 2)出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。
【示例代码】:
package com.dfbz.demo01_线程的等待与唤醒;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo10_线程状态_TERMINATED {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("t1 running...");
}
};
t1.start();
// 让当前线程睡眠10毫秒,确保t1线程任务执行完毕
Thread.sleep(10);
System.out.println(t1.getState()); // TERMINATED
}
}
2.2 线程状态变化
线程流程图:
2.2.1 线程状态的转变
接下来我们将来分析各自状态之间的转换,其实主要就是 Blocked、waiting、Timed Waiting 三种状态的转换 ,以及他们是如何进入下一状态最终进入 Runnable
1)Runnable与Blocked状态转换
- 1)Runnable状态进入Blocked状态:当前线程竞争锁对象失败时:
Runnable-->Blocked
- 2)Blocked状态进入Runnable状态:当之前的线程释放了锁对象时,新的线程竞争到了锁对象时:
Blocked-->Runnable
【示例代码】:
package com.dfbz.demo02_线程状态的改变;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_Runnable进入Blocked状态 {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(() -> {
synchronized (obj) {
while (true) {
System.out.println("t1");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (obj) {
while (true) {
System.out.println("t2");
}
}
}, "t2");
t1.start();
t2.start();
}
}
2)Runnable与Waiting状态转换
如果通过其他线程调用 notify() 或 notifyAll()来唤醒它,则它会直接进入Blocked状态,这里可能会有疑问,不是应该直接进入 Runnable 吗?
这里需要注意一点 ,因为唤醒 Waiting 线程的线程如果调用 notify() 或 notifyAll(),要求必须首先持有该锁,这也就是我们说的 wait()、notify 必须在 synchronized 代码块中。
所以处于 Waiting 状态的线程被唤醒时拿不到该锁,就会进入 Blocked 状态,直到执行了 notify()/notifyAll() 的唤醒它的线程执行完毕并释放锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从 Blocked 状态回到 Runnable 状态。
【示例代码】:
package com.dfbz.demo02_线程状态的改变;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(() -> {
synchronized (obj) {
System.out.println("t1-before..");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1-after..");
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (obj) {
System.out.println("t2-before..");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2-after..");
}
}, "t2");
t1.start();
t2.start();
new Thread(){
@Override
public void run() {
synchronized (obj){
// 将所有线程都唤醒,唤醒的线程都将处于blocked状态,因为要等当前线程释放锁对象
obj.notifyAll();
}
}
}.start();
}
}
Tips:当我们通过 notify 唤醒时,是先进入锁阻塞状态的 ,再等抢夺到锁后才会进入 Runnable 状态!
Runnable状态进入Waiting状态有三种情况:
- 1)调用wait方法进入Waiting状态
- 返回Runnable状态使用notifly或notifyAll方法(需要注意的是先进入Blocked再进入Runnable)
- 2)调用join方法进入Waiting状态
- 返回Runnable状态使用interrupt()方法中断线程
- 3)调用park方法进入Waiting状态
- 返回Runnable状态使用interrupt()方法中断线程
【示例代码1】:
package com.dfbz.demo02_线程状态的改变;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(() -> {
synchronized (obj) {
System.out.println("t1-before..");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1-after..");
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (obj) {
System.out.println("t2-before..");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2-after..");
}
}, "t2");
t1.start();
t2.start();
new Thread(){
@Override
public void run() {
synchronized (obj){
// 将所有线程都唤醒,唤醒的线程都将处于blocked状态,因为要等当前线程释放锁对象
obj.notifyAll();
}
}
}.start();
}
}
【示例代码2】:
package com.dfbz.demo02_线程状态的改变;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
while (true){
}
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
System.out.println("t2-before...");
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2-after...");
}
};
t1.start();
t2.start();
// 让当前线程睡眠0.1s,确保t1线程能够执行join
Thread.sleep(100);
System.out.println(t1.getState()); // RUNNABLE
System.out.println(t2.getState()); // WAITING
System.out.println(t2.isInterrupted());
// 打断t2线程,t2线程将会处于Runnable状态
t2.interrupt();
System.out.println(t2.isInterrupted());
}
}
【示例代码3】:
package com.dfbz.demo02_线程状态的改变;
import java.util.concurrent.locks.LockSupport;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
while (true){
System.out.println("t1-before");
LockSupport.park();
System.out.println("t1-after");
}
}
};
t1.start();
// 线程中断后,将会t1线程将会从Waiting状态转换为Runnable状态
t1.interrupt();
}
}
3)Runnable与Timed Waiting状态转换
Runnable状态进入Timed Waiting状态有三种情况:
-
1)线程执行了设置了时间参数的 Thread.sleep(long millis) 方法;
- 返回Runnable状态:
- 1)等到millis时间到达
- 2)使用interrupt中断线程(中断标记还是false)
- 返回Runnable状态:
-
2)线程执行了设置了时间参数的 Object.wait(long timeout) 方法;
- 返回Runnable状态:
- 1)等到timeout时间到达
- 2)使用interrupt中断线程(中断标记还是false)
- 3)使用notifly或notiflyAll唤醒(首先进入Blocked状态,竞争到锁之后再进入Runnable状态)
- 返回Runnable状态:
-
3)线程执行了设置了时间参数的 Thread.join(long millis) 方法;
- 1)等到timeout时间到达
- 2)使用interrupt中断线程(中断标记还是false)
- 3)使用notifly或notiflyAll唤醒(首先进入Blocked状态,竞争到锁之后再进入Runnable状态)
Tips:notifly和notiflyAll只能唤醒使用wait方法进行等待的线程;
【示例代码1】:
1)测试sleep方法时间到达后线程状态从Timed Waiting变为Runnable
2)测试调用interrupt()方法将线程状态从Timed Waiting变为Runnable
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
System.out.println("t1-before...");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1-after...");
}
};
t1.start();
// 中断t1线程(t1线程的中断标记还是false),t1线程抛出异常后又恢复为Runnable状态
t1.interrupt();
}
}
【示例代码2-1】:
1)测试wait方法时间到达后线程状态从Timed Waiting变为Runnable
2)测试调用interrupt()方法将线程状态从Timed Waiting变为Runnable
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
synchronized (Object.class){
System.out.println("t1-before...");
try {
// 等到wait的时间到达之后线程自动从Timed Waiting状态变为Runnable状态
Object.class.wait(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1-after...");
}
}
};
t1.start();
// 中断t1线程(t1线程的中断标记还是false),t1线程抛出异常后又恢复为Runnable状态
t1.interrupt();
}
}
【示例代码2-2】:使用notify/notiflyAll将线程状态从Timed Waiting变为Runnable(Blocked)
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
synchronized (Object.class){
System.out.println("t1-before...");
try {
// 等到wait的时间到达之后线程自动从Timed Waiting状态变为Runnable状态
Object.class.wait(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1-after...");
}
}
};
t1.start();
// 让main线程睡眠10毫秒,确保t1线程能够先执行
Thread.sleep(10);
synchronized (Object.class){
Object.class.notifyAll();
}
}
}
【示例代码3】:
1)测试join方法时间到达后线程状态从Timed Waiting变为Runnable
2)测试调用interrupt()方法将线程状态从Timed Waiting变为Runnable
public class Demo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
while (true){}
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
try {
t1.join(3000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2-after..");
}
};
t1.start();
t2.start();
// 让main线程睡眠10毫秒,确保t1和t2线程先执行
Thread.sleep(10);
t2.interrupt();
}
}
2.2.2 状态变化的注意事项
- 1)线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。
- 2)线程生命周期不可逆,一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。
- 3)所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换。也就是这两个状态不会参与相互转化