从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

    • 反射

    • 注解

    • 集合类

    • 线程

      • 进程和线程的基本概念
      • Java 中的线程
      • Thread 类的方法
      • Runnable 接口
      • 匿名内部类方式实现线程的创建
      • 线程安全问题
      • 线程状态
        • 线程的六种状态
        • Timed Waiting(计时等待)
        • BLOCKED(锁阻塞)
        • Waiting(无限等待)
        • 补充
      • 等待唤醒机制
      • 线程池
  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaSenior
  • 线程
2024-01-26
目录

线程状态

# 线程状态

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。

# 线程的六种状态

在线程的生命周期中,有几种状态呢?在 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(计时等待)

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();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

通过案例可以发现,sleep 方法的使用还是很简单的。我们需要记住下面几点:

  1. 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协作关系。
  2. 为了让其他线程有机会执行,可以将 Thread.sleep() 的调用放线程 run() 之内。这样才能保证该线程执行过程中会睡眠
  3. sleep 与锁无关,线程睡眠到期自动苏醒,并返回到 Runnable(可运行)状态。

小提示:sleep() 中指定的时间是线程不会运行的最短时间。因此,sleep() 方法不能保证该线程睡眠到期后就开始立刻执行。

Timed Waiting 线程状态图:

# BLOCKED(锁阻塞)

Blocked 状态在 API 中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。

我们已经学完同步机制,那么这个状态是非常好理解的了。比如,线程 A 与线程 B 代码中使用同一锁,如果线程 A 获取到锁,线程 A 进入到 Runnable 状态,那么线程 B 就进入到 Blocked 锁阻塞状态。

这是由 Runnable 状态进入 Blocked 状态。除此 Waiting 以及 Time Waiting 状态也会在某种情况下进入阻塞状态,而这部分内容作为扩充知识点带领大家了解一下。

Blocked 线程状态图:

# Waiting(无限等待)

我们重点来看看这个状态。线程之间通信时,常常会用到这两个方法。

Wating 状态在 API 中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态,这个状态我们之前并没有学过。

Waiting 线程状态图:

举个生活中的例子:

  • 顾客到包子铺,说明要买什么包子
  • 说完后,顾客就开始等待老板做包子(调用 wait 方法)
  • 老板开始做包子,做好后告诉顾客(调用 notify 方法)

我们通过代码来模拟:

  1. 创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用 wait 方法,放弃 CPU 的执行,进入到 WAITING 状态(无限等待)
  2. 创建一个老板线程(生产者):花了 5 秒做包子,做好包子之后,调用 notify 方法,唤醒顾客吃包子

注意事项:

  1. 顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
  2. 同步使用的锁对象必须保证唯一
  3. 只有锁对象才能调用 wait 和 notify 方法

示例代码:

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();

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

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();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

至此,线程进入 Time Waiting 有两种方式:

  • 使用 Thread.sleep(lone m) 方法
  • 使用 wait (lone m) 方法

唤醒也有两种方式:

  • notify() 唤醒等待中的线程(如果有多个,则通过调度唤醒其中一个)
  • notifyAll() 唤醒全部等待中的线程

# 补充

到此为止我们已经对线程状态有了基本的认识,想要有更多的了解,详情可以见下图:

上次更新: 2024/10/1 19:22:42
线程安全问题
等待唤醒机制

← 线程安全问题 等待唤醒机制→

最近更新
01
语雀文档一键下载至本地教程
07-04
02
要成功,就不要低估环境对你的影响
07-03
03
血泪教训:电子设备要定期开机
07-02
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式