搜索 K
Appearance
博客正在加载中...
Appearance
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。
在线程的生命周期中,有几种状态呢?在 API 中 java.lang.Thread.State 这个枚举(Thread 的内部类)中给出了六种线程状态:
| 线程状态 | 导致状态发生的条件 |
|---|---|
| NEW(新建) | 线程刚被创建,但是并未启动,还没调用 start 方法 |
| Runnable(可运行) | 线程可以在 Java 虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器 |
| Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态;当该线程持有锁时,该线程将变成 Runnable 状态。 |
| Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用 Object.notify 或者 Object.notifyAll 方法才能够唤醒 |
| Timed Waiting(计时等待) | 同 waiting 状态,有几个方法有超时参数,调用它们将进入 Timed Waiting 状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait。 |
| Teminated(被终止) | 因为 run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡 |
这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。
我们不需要去研究这几种状态的实现原理,我们只需知道在做线程操作中存在这样的状态。那我们怎么去理解这几个状态呢?新建与被终止还是很容易理解的,我们就研究一下线程从 Runnable(可运行)状态与非运行状态之间的转换问题。
注意,阻塞状态是由于锁,被动放弃了 CPU 的控制权。而 Waiting 和 Timed Waiting 则是主动放弃 CPU 控制权。
Timed Waiting 在 API 中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。单独的去理解这句话,真是玄之又玄,其实我们在之前的操作中已经接触过这个状态了,在哪里呢?
在我们写卖票的案例中,为了减少线程执行太快,现象不明显等问题,我们在 run 方法中添加了 sleep 语句,这样就强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。
其实当我们调用了 sleep 方法之后,当前执行的线程就进入到“休眠状态”,其实就是所谓的 Timed Waiting(计时等待)
那么我们通过一个案例加深对该状态的一个理解:实现一个计数器,计数到 100,在每个数字之间暂停 1 秒,每隔 10 个数字输出一个字符串。示例代码如下。
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
if ((i) % 10 == 0) {
System.out.println("‐‐‐‐‐‐‐" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 线程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new MyThread().start();
}
}通过案例可以发现,sleep 方法的使用还是很简单的。我们需要记住下面几点:
Thread.sleep() 的调用放线程 run() 之内。这样才能保证该线程执行过程中会睡眠小提示:sleep() 中指定的时间是线程不会运行的最短时间。因此,sleep() 方法不能保证该线程睡眠到期后就开始立刻执行。
Timed Waiting 线程状态图:

Blocked 状态在 API 中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
我们已经学完同步机制,那么这个状态是非常好理解的了。比如,线程 A 与线程 B 代码中使用同一锁,如果线程 A 获取到锁,线程 A 进入到 Runnable 状态,那么线程 B 就进入到 Blocked 锁阻塞状态。
这是由 Runnable 状态进入 Blocked 状态。除此 Waiting 以及 Time Waiting 状态也会在某种情况下进入阻塞状态,而这部分内容作为扩充知识点带领大家了解一下。
Blocked 线程状态图:

我们重点来看看这个状态。线程之间通信时,常常会用到这两个方法。
Wating 状态在 API 中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态,这个状态我们之前并没有学过。
Waiting 线程状态图:

举个生活中的例子:

我们通过代码来模拟:
注意事项:
示例代码:
package chapter200Thread;
public class Demo09WaitAndNotify {
public static void main(String[] args) {
// 创建锁对象,保证唯一
Object obj = new Object();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
// 保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("告知老板要的包子的种类和数量");
// 调用wait()方法,放弃cpu的执行,进入到等待状态
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 唤醒之后执行的代码
System.out.println("包子已经做好,开吃!");
}
}
}.start();
// 创建一个老板线程(生产者)
new Thread(){
@Override
public void run() {
// 模拟5秒做包子
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("包子已经做好,告知顾客可以吃包子了");
// 调用notify()方法,唤醒顾客线程
obj.notify();
}
}
}.start();
}
}wait 方法还可以传参,参数是毫秒值。如果在毫秒值结束后还没被 notify 唤醒,就会自动醒来。
示例:
package chapter200Thread;
public class Demo10WaitTime {
public static void main(String[] args) {
// 创建锁对象,保证唯一
Object obj = new Object();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
// 保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("告知老板要的包子的种类和数量");
// 调用wait()方法,放弃cpu的执行,进入到等待状态
try {
obj.wait(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 唤醒之后执行的代码
System.out.println("包子已经做好,开吃!");
}
}
}.start();
}
}至此,线程进入 Time Waiting 有两种方式:
Thread.sleep(lone m) 方法wait (lone m) 方法唤醒也有两种方式:
notify() 唤醒等待中的线程(如果有多个,则通过调度唤醒其中一个)notifyAll() 唤醒全部等待中的线程到此为止我们已经对线程状态有了基本的认识,想要有更多的了解,详情可以见下图: