Java基础教程:多线程基础——线程的状态
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状态,要优雅很多。
- 如果设置等待时间的话,超过时间,会自动被唤醒。
终止状态
线程所有逻辑执行完了,就处于终止状态。这个和初始态一样都是好理解的!