Java 线程状态流转图
2021-05-11 20:29
标签:循环 理论 状态 注意 创建线程 ext 异常 row http Java的线程可以有多种状态,在Thread.State类中定义了6个常量来表示线程的状态,分别是NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,下面是比较详细的一幅状态流转图: 先看下面一段代码,测试sleep的时候是否会释放已经获取到的资源: 运行结果如下: 从输出结果可以可以看到,t1和t2同时启动,t2先进入同步块,获取到monitor后,t2继续执行,进行10秒sleep,休眠过程中t2处于TIMED_WAITING状态。 与此同时,由于t1未获取到monitor,只能阻塞等待t2释放monitor,此时t1处于BLOCKED状态; 等待10秒过后,t1 sleep结束,t1退出同步代码块,t2这才获取到monitor。 根据上面的例子总结一下,有以下几点: 1.线程进行sleep操作时,不会释放已经获取的资源; 2.线程进行sleep时,线程状态为TIMED_WAITING; 3.因为没有获取到monitor而发生线程阻塞,此时线程处于BLOCKED状态。 wait和后面的notify都是针对monitor来说的,可以进行线程间的通信。 下面介绍 运行上面的代码,运行结果如下: 从上面的运行结果可以看出,2个线程同时执行doWait,虽然有同步块synchronized(monitor),但是两个线程并没有发生阻塞,几乎同时获取到了monitor,获取到monitor后,t1和t2执行wait操作,此时两个线程的状态为WAITING状态;t3启动后,执行doNotify,同样能够获取都monitor,并进行notify操作,notify后,t2的wait状态接触。 根据这个例子,总结一下: 1.调用monitor的wait方法,会释放synchronized中的monitor,所以其他线程也可以获取到monitor,这点与sleep不同; 2.调用monitor.wait方法后,线程处于WAITING状态; 3.可以调用monitor.notify()或者monitor.notifyAll()来通知调用了monitor.wait()的线程,只不过notify()只会通知一个线程,并且这个随意选择一个线程通知,并不是按照wait顺序进行notify。 suspend挂起线程,resume让一个挂起的线程继续执行 运行结果,这里运行了10次,发现会有不同的结果: 运行结果1如下 从这个结果中可以看出,t1和t2同时执行doWork,但是t1先获取到monitor,进入同步代码块,然后让t1调用suspend,将t1挂起,可以看到suspend让线程挂起的时候,并没有释放拥有的monitor,导致t2阻塞处于BLOCKED状态,而调用resume后的线程状态是RUNNABLE状态。 注意结果1中可以看到,resume的顺序和suspend的顺序相同,即t1 suspend后,再resume t1;t2 suspend后,再resume t2;而后面马上给出的运行结果2,就出现resume顺序和suspend顺序不相同的情况: 运行结果2如下: 从结果2中可以看出, 1.t1、t2进入doWork,t2先获取到monitor进入同步代码块,t1由于未获取到monitor处于BLOCKED状态,t2由于调用了suspend方法(挂起)而处于RUNNABLE状态; 2.先resume t1,但是t1的状态还是BLOCKED,然后resume t2,t2的挂起状态解除,t2释放monitor,t2完成任务后处于TERMINATED状态; 3.t1获取到monitor,t1执行suspend,一直到程序结束,t1都处于RUNNABLE状态,而不是预期的TERMINATED状态,这是因为resume t2的操作发生在 suspend t2之前。 根据这段代码,以及这两个运行结果,可以得出下面的结论: 1.调用suspend的线程不会释放已经拥有的资源; 2.调用suspend后,且未调用resume时,线程处于RUNNABLE状态(就绪); 3.resume可以发生在suspend之前,且不会抛出异常,只不过此时suspend线程将一直保持RUNNABLE状态。 4.一定要防止出现resume发生在suspend之前,其实suspend和resume已经被废弃了,所以不使用这两个方法就ok了。 join可以实现,等待一个线程结束后,再执行后面的操作,看下面的示例代码: 运行结果如下: 从上面的运行结果得出以下结论: 1.调用t1.join()方法后,需要等待t1线程执行完毕后,t1.join()后面的语句才会执行; 2.t2在等待t1执行完毕的过程中,t2处于WAINTING状态。 yield的功能:让当前线程“让”出cpu,注意,只是让出CPU,而不会释放已经获取到资源,看下面的示例即可: 运行代码,结果如下: 可以看到,t1和t2同时进入doWork,但是t1先获取到monitor进入同步代码块,然后t1一直死循环进行yield,而yield的时候,并没有释放资源(monitor)。 所以,yield理论上会“礼让”CPU,但是礼让CPU的同时,并不会释放已经获取的资源。 park用于让当前线程等待(WAITING),unpark将处于等待的线程恢复正常。 输出结果如下: 从上面的运行结果可以看出,线程调用park后阻塞,线程进入WAITING状态,当调用unpark的时候,并没有抛出InterruptedException。 Java 线程状态流转图 标签:循环 理论 状态 注意 创建线程 ext 异常 row http 原文地址:https://www.cnblogs.com/-beyond/p/13143235.html一.线程状态流转图
二.示例代码
2.1 sleep
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
@Slf4j
public class TestSleep {
// 创建一个对象,作为synchronized的monitor(锁)
private static Object obj = new Object();
/**
* 创建线程要完成的操作
*/
public void work() {
log.info("进入work,尝试获取monitor(obj)");
// 获取监视器(monitor)
synchronized (obj) {
log.info("获取到monitor");
try {
// 进入的线程进行阻塞
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 测试sleep操作是否会释放已经获取的资源
*/
@Test
public void test() throws InterruptedException {
Runnable runnable = this::work;
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
// 主线程阻塞等待t1和t2执行
TimeUnit.SECONDS.sleep(1);
log.info("t1的状态:{}, t2的状态:{}", t1.getState(), t2.getState());
TimeUnit.SECONDS.sleep(100);
}
}
INFO 2020-06-16 18:08:14 [Thread-2] - 进入work,尝试获取monitor(obj)
INFO 2020-06-16 18:08:14 [Thread-1] - 进入work,尝试获取monitor(obj)
INFO 2020-06-16 18:08:14 [Thread-2] - 获取到monitor
INFO 2020-06-16 18:08:15 [main] - t1的状态:BLOCKED, t2的状态:TIMED_WAITING
INFO 2020-06-16 18:08:24 [Thread-1] - 获取到monitor
2.2 wait与notify
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
/**
* 描述:
*
* @author ganlixin
* @create 2020-06-16
*/
@Slf4j
public class TestWaitAndNotify {
private static Object monitor = new Object();
@Test
public void test() throws InterruptedException {
Runnable doWaitRunnable = this::doWait;
Runnable doNotifyRunnable = this::doNotify;
// 两个线程执行相同的doWait,观察是否有线程阻塞
Thread t1 = new Thread(doWaitRunnable);
Thread t2 = new Thread(doWaitRunnable);
Thread t3 = new Thread(doNotifyRunnable);
// t1和t2会进行wait
t1.start();
t2.start();
// 休眠3秒,等待t1和t2运行起来
TimeUnit.SECONDS.sleep(3);
log.info("t1的状态:{}, t2的状态:{}", t1.getState(), t2.getState());
// 启动t3,进行notify
t3.start();
TimeUnit.SECONDS.sleep(10);
}
private void doWait() {
log.info("doWait -> 进入到doWait");
synchronized (monitor) {
log.info("doWait -> 获取到monitor");
try {
// 阻塞
monitor.wait();
log.info("doWait -> wait状态解除");
} catch (InterruptedException e) {
log.info("doWait -> 捕获InterruptedException,e=", e);
}
}
}
public void doNotify() {
log.info("doNotify -> 进入doNotify");
synchronized (monitor) {
log.info("doNotify -> 获取到monitor,开始notify");
// notify会随机选择一个调用了monitor.wait而等待线程
monitor.notify();
// notifyAll,会通知所有调用了monitor.wait的线程
// monitor.notifyAll();
log.info("doNotify -> notify完成");
}
}
}
INFO 2020-06-16 19:07:48 [Thread-2] - doWait -> 进入到doWait
INFO 2020-06-16 19:07:48 [Thread-1] - doWait -> 进入到doWait
INFO 2020-06-16 19:07:48 [Thread-1] - doWait -> 获取到monitor
INFO 2020-06-16 19:07:48 [Thread-1] - doWait -> 获取到monitor
INFO 2020-06-16 19:07:51 [main] - t1的状态:WAITING, t2的状态:WAITING
INFO 2020-06-16 19:07:51 [Thread-3] - doNotify -> 进入doNotify
INFO 2020-06-16 19:07:51 [Thread-3] - doNotify -> 获取到monitor,开始notify
INFO 2020-06-16 19:07:51 [Thread-3] - doNotify -> notify完成
INFO 2020-06-16 19:07:51 [Thread-2] - doWait -> wait状态解除
2.3suspend与resume
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
@Slf4j
public class TestSuspendAndResume {
private static Object monitor = new Object();
@Test
public void test() throws InterruptedException {
Runnable runnable = this::doWork;
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
// 主线程休眠3秒,等待t1和t2执行
TimeUnit.SECONDS.sleep(3);
log.info("t1状态:{}, t2状态:{}", t1.getState(), t2.getState());
log.info("开始resume t1");
t1.resume();
TimeUnit.SECONDS.sleep(1);
log.info("t1状态:{}", t1.getState());
log.info("开始resume t2");
t2.resume();
TimeUnit.SECONDS.sleep(1);
log.info("t2状态:{}", t2.getState());
TimeUnit.SECONDS.sleep(2);
log.info("t1状态:{}, t2状态:{}", t1.getState(), t2.getState());
}
public void doWork() {
log.info("doWork -> 进入doWork,尝试获取monitor");
synchronized (monitor) {
log.info("doWork -> 获取到monitor");
// suspend挂起线程,接口已经是deprecated
Thread.currentThread().suspend();
log.info("doWork -> 挂起状态解除");
}
}
}
INFO 2020-06-16 19:52:47 [Thread-1] - doWork -> 进入doWork,尝试获取monitor
INFO 2020-06-16 19:52:47 [Thread-2] - doWork -> 进入doWork,尝试获取monitor
INFO 2020-06-16 19:52:47 [Thread-1] - doWork -> 获取到monitor
INFO 2020-06-16 19:52:50 [main] - t1状态:RUNNABLE, t2状态:BLOCKED
INFO 2020-06-16 19:52:50 [main] - 开始resume t1
INFO 2020-06-16 19:52:50 [Thread-1] - doWork -> 挂起状态解除
INFO 2020-06-16 19:52:50 [Thread-2] - doWork -> 获取到monitor
INFO 2020-06-16 19:52:51 [main] - t1状态:TERMINATED
INFO 2020-06-16 19:52:51 [main] - 开始resume t2
INFO 2020-06-16 19:52:51 [Thread-2] - doWork -> 挂起状态解除
INFO 2020-06-16 19:52:52 [main] - t2状态:TERMINATED
INFO 2020-06-16 19:52:54 [main] - t1状态:TERMINATED, t2状态:TERMINATED
INFO 2020-06-16 19:58:34 [Thread-2] - doWork -> 进入doWork,尝试获取monitor
INFO 2020-06-16 19:58:34 [Thread-1] - doWork -> 进入doWork,尝试获取monitor
INFO 2020-06-16 19:58:34 [Thread-2] - doWork -> 获取到monitor
INFO 2020-06-16 19:58:37 [main] - t1状态:BLOCKED, t2状态:RUNNABLE
INFO 2020-06-16 19:58:37 [main] - 开始resume t1
INFO 2020-06-16 19:58:38 [main] - t1状态:BLOCKED
INFO 2020-06-16 19:58:38 [main] - 开始resume t2
INFO 2020-06-16 19:58:38 [Thread-2] - doWork -> 挂起状态解除
INFO 2020-06-16 19:58:38 [Thread-1] - doWork -> 获取到monitor
INFO 2020-06-16 19:58:39 [main] - t2状态:TERMINATED
INFO 2020-06-16 19:58:41 [main] - t1状态:RUNNABLE, t2状态:TERMINATED
2.4 join
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
@Slf4j
public class TestJoin {
@Test
public void test() throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
log.info("开始执行");
try {
log.info("进行sleep 3秒");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("执行完毕");
}
};
Thread t2 = new Thread() {
@Override
public void run() {
log.info("开始执行");
try {
log.info("等待t1完成");
t1.join();
log.info("已经等到t1完成工作,t2继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("执行完毕");
}
};
t1.start();
t2.start();
TimeUnit.SECONDS.sleep(1);
log.info("t1状态:{}, t2状态:{}", t1.getState(), t2.getState());
TimeUnit.SECONDS.sleep(10);
}
}
INFO 2020-06-16 20:27:54 [Thread-1] - 开始执行
INFO 2020-06-16 20:27:54 [Thread-2] - 开始执行
INFO 2020-06-16 20:27:54 [Thread-2] - 等待t1完成
INFO 2020-06-16 20:27:54 [Thread-1] - 进行sleep 3秒
INFO 2020-06-16 20:27:55 [main] - t1状态:TIMED_WAITING, t2状态:WAITING
INFO 2020-06-16 20:27:57 [Thread-1] - 执行完毕
INFO 2020-06-16 20:27:57 [Thread-2] - 已经等到t1完成工作,t2继续执行
INFO 2020-06-16 20:27:57 [Thread-2] - 执行完毕
2.5 yield
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
@Slf4j
public class TestYield {
private static Object monitor = new Object();
@Test
public void test() throws InterruptedException {
Runnable runnable = this::doWork;
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
TimeUnit.SECONDS.sleep(2);
log.info("t1状态:{}, t2状态:{}", t1.getState(), t2.getState()); // RUNNABLE
TimeUnit.SECONDS.sleep(10);
}
public void doWork() {
log.info("doWork -> 进入doWork");
synchronized (monitor) {
log.info("doWork -> 获取到monitor,进行死循环yield");
while (true) {
Thread.yield();
}
}
}
}
INFO 2020-06-16 20:50:48 [Thread-2] - doWork -> 进入doWork
INFO 2020-06-16 20:50:48 [Thread-1] - doWork -> 进入doWork
INFO 2020-06-16 20:50:48 [Thread-2] - doWork -> 获取到monitor,进行死循环yield
INFO 2020-06-16 20:50:50 [main] - t1状态:BLOCKED, t2状态:RUNNABLE
2.6 park和unpark
package cn.ganlixin.thread;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* 描述:
*
* @author ganlixin
* @create 2020-06-16
*/
@Slf4j
public class TestParkAndUnpark {
@Test
public void test() throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
log.info("开始park");
LockSupport.park();
log.info("WAITING状态解除");
}
};
t1.start();
// 让t1先执行几秒
TimeUnit.SECONDS.sleep(2);
log.info("t1的状态:{}", t1.getState());
log.info("开始执行unpark t1");
LockSupport.unpark(t1);
TimeUnit.SECONDS.sleep(20);
}
}
INFO 2020-06-16 23:38:26 [Thread-1] - 开始park
INFO 2020-06-16 23:38:28 [main] - t1的状态:WAITING
INFO 2020-06-16 23:38:28 [main] - 开始执行unpark t1
INFO 2020-06-16 23:38:28 [Thread-1] - WAITING状态解除