线程的几种状态
线程有那几种状态
线程的状态是一个枚举类型(一个特殊的类,一般表示一组常量)。
尝试用代码打印所有线程状态
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
从打印结果可以看出java线程一共有6种状态,分别为NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED。
每种状态表示的含义
- 新建(NEW):线程对象被创建后,就处于新建状态。此时,线程对象还没有分配到CPU运行。
- 可运行(RUNNABLE):线程对象已经分配到CPU运行,但是还没有开始执行。调用start()方法后,线程就进入了可运行状态。
- 阻塞(BLOCKED):线程正在等待锁的释放或者I/O操作完成,此时无法执行。当一个线程试图获取一个已经被其他线程占用的锁时,就会进入阻塞状态。
- 等待(WAITING):线程在进入阻塞状态前,会先进入等待状态。等待状态下的线程需要被其他线程唤醒才能继续执行。
- 超时等待(TIMED_WAITING):线程在进入等待状态一段时间后,如果没有其他线程唤醒它,就会进入超时等待状态。超时等待状态下的线程会在指定的时间后自动醒来并重新进入可运行状态。
- 终止(TERMINATED):线程执行完毕或者因异常而结束,就进入了终止状态。此时,线程对象不再可用。
状态与状态之间的转换
NEW -RUNNABLE-TERMINATED状态的转换
public class test0 {
public static void main(String[] args) {
// 创建一个新线程t,使用Lambda表达式指定线程的任务为空循环
Thread t = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
}
});
// 设置线程的名称为"线程t"
t.setName("线程t");
// 打印当前线程的名称和状态
System.out.println(t.getName() + "状态" + t.getState());
// 启动线程t
t.start();
// 当线程t仍然存活时,循环打印线程的名称和状态
while (t.isAlive()) {
System.out.println(t.getName() + "状态" + t.getState());
}
// 再次打印线程的名称和状态,此时线程t应该已经终止
System.out.println(t.getName() + "状态" + t.getState());
}
}
这段代码创建了一个新线程t,并使用Lambda表达式指定了线程的任务为一个空循环。然后,通过调用setName方法设置了线程的名称为"线程t"。接下来,通过调用start方法启动了线程t。在主线程中,使用while循环不断检查线程t是否仍然存活,并通过System.out.println打印线程的名称和状态。最后,再次打印线程的名称和状态,此时线程t应该已经终止。
请注意,由于线程的状态变化是异步的,所以在实际运行中,线程的状态可能会在打印之间发生变化。
WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
public class test1 {
public static void main(String[] args) {
Object o = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o) {
System.out.println("线程t2");
}
}
}, "t2");
t2.start();
}
}
首先,主线程创建一个新的对象o,然后创建线程t1,在该线程中定义了一个匿名内部类实现了Runnable接口。在run()方法中,使用synchronized关键字对对象o进行同步,然后在一个无限循环中让线程睡眠1秒钟。这意味着线程t1会一直执行这个循环,不断休眠。
接下来,主线程创建线程t2,并在该线程的run()方法中使用synchronized关键字对对象o进行同步。然后输出字符串"线程t2"。
最后,主线程调用start()方法启动线程t1和t2。由于线程t1中的无限循环会一直占用CPU资源,而线程t2中的同步块需要获取对象o的锁才能执行,因此线程t2会被阻塞,直到线程t1释放了对象o的锁。
当线程t1执行完无限循环后,它会退出同步块,然后线程t2可以获取到对象o的锁并继续执行输出语句。
使用 jconsole 可以看到 t1 的状态是 TIMED_WAITING , t2 的状态是 BLOCKED
把上述代码中的sleep方法换成wait方法t1就会由有限时间唤醒等待变为无限时间唤醒等待了 t1的状态就会变为WAITING。