Java基础教程:多线程基础——线程的状态
线程的状态
在Java中,线程有6种状态,分别为:
- 初始:NEW
- 运行:RUNNABLE
- 阻塞:BLOCKED
- 等待:WAITING
- 超时等待:TIMED_WAIT
- 终止:TERMINAL
这六种状态分别对应于Thread.State中的枚举类型。可以用下面这张图来解释一下Java中的线程的状态转换
初始态
初始态表示一个线程刚被初始化,即new Thread()。
Thread thread = new Thread();
System.out.println(thread.getState());
//Output:NEW
这个没什么好说的,也是最简单。
运行态
当调用一个Thread对象的start方法后,该线程进入运行态。运行态的名字是很有迷惑性的,其实运行态再细分还可以分为两个子状态:
- Ready:调用start后,该线程放入可运行线程池中,等待被调度,获得CPU运行权
- Running:获得CPU时间片后变为运行中状态
也即是就绪和运行中都是运行态,一定要谨记!
阻塞态
阻塞态可能理解就要上一个台阶了,阻塞态表示一个线程因为等待临界区的锁而被阻塞产生的状态。我们举一个例子,我们有一个上锁的单人厕所,有两个人同时要求上厕所,但是有一个人抢先进去了,然后反锁了,第二个就在厕所门口等着,他就属于阻塞态。此时又来了第三个人、第四个等等,他们都属于阻塞态,只有第一个出来,下一个进去的才能摆脱这个状态!
话不多说,我们写代码来实现这情形,在这里用了Lambda表达式来简写线程的run方法。
public class BlockedDemo {
public static void main(String[] args) throws InterruptedException {
Toilet room = new Toilet();
Thread a = new Thread(() -> room.use());
a.start();
Thread.sleep(1000);
Thread b = new Thread(() -> room.use());
b.start();
Thread.sleep(2000);
System.out.println(b.getState());
}
}
class Toilet{
synchronized void use(){
try {
Thread.sleep(7000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
此时打印出来的状态就是:BLOCKED状态!
等待态与超时等待态
当我们拿到锁执行方法的时候,我们可能由于某些原因,还是以厕所为例,发现没带纸,我们需要出去,这时肯定不能霸占厕所,所以我们要主动出去,遂调用wait方法后,让出锁,我们就处于等待状态。如果送纸的人来了,再把我们唤醒notify。
public class WaitingDemo {
public static void main(String[] args) throws InterruptedException {
WaitToilet room = new WaitToilet();
Thread a = new Thread(() -> room.use());
a.start();
Thread.sleep(100);
System.out.println(a.getState());
}
}
class WaitToilet{
synchronized void use(){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
你可以看到wait(long time)有一个参数指定时间,表示我们先出去等time时间,然后再排队如厕,此时我们就处于超时等待状态。如果送纸的人迟迟不来,也不等了,
我们再来简单总结一下:
- 一个线程A在拿到锁但不满足执行条件的时候,需要另一个线程B去满足这个条件,那么线程A就会释放锁并处于waiting的状态,等线程B执行完再执行。
- waiting状态的好处是:此状态的线程不再活动,不再参与调度,因此不会浪费 CPU 资源,也不会去竞争锁了,相比暴力的blocking状态,要优雅很多。
- 如果设置等待时间的话,超过时间,会自动被唤醒。
终止状态
线程所有逻辑执行完了,就处于终止状态。这个和初始态一样都是好理解的!