17 | 线程的生命周期和状态转移

↑↑↑ 欢迎 点赞、关注、收藏!!!,10 年 IT 行业老鸟,持续分享更多 IT 干货

注: 本笔记为 公司内部技术小组持续学习 2 年多时间 + 个人整理不下 5 次的结果产出。

目录

17 | 线程的生命周期和状态转移

每日一问

回答

分析

扩展

线程是什么?

线程实现

线程状态

线程使用

分享: Java 纤程库 - Quasar

Thread vs Quasar

Project Loom


17 | 线程的生命周期和状态转移


每日一问

一个线程两次 start() 方法会出现什么情况?谈谈线程的生命周期和状态转移?


回答

  1. Java 的线程是不允许启动两次的,第二次必然会抛出 IllegalThreadStateException。这是一种运行时异常,多次调用 start 被认为是编程错误。

  2. Java 5 以后,线程状态明确定义在 java.lang.Thread.State 中,分别是:

    • 新建(NEW)

      • 线程被创建出来,还没有真正的启动状态。

    • 就绪(RUNNABLE)

      • 表示该线程已经在 JVM 中执行,可能正在运行,也可能还在等待 CPU 资源,在就绪队列里排队。

      • Java 线程中将 就绪(READY)和运行中(RUNNING)两种状态笼统的称为 RUNNABLE

        就绪(READY):线程对象创建后,其他线程(比比如main线程)调用了该对象的 start() 方法。该状态的线程位于可运行线程池中,等待被线程调度选中并分配cpu使用权。 运行中(RUNNING):就绪(READY)的线程获得了 CPU 时间片,开始执行程序代码。

    • 阻塞(BLOCKED)

      • 线程在等待 Monitor lock。

    • 等待(WAITING)

      • 表示正在等待其他线程采取某些操作。无限期等待。

    • 计时等待(TIMED_WAIT)

      • 与等待类似,但存在超时条件。

    • 终止(TERMINATED)

      • 线程终止运行。


分析

  1. 线程是什么?及 Java 底层实现方式。

  2. 线程状态切换,及和锁等并发工具类的互动。

  3. 线程编程时容易踩的坑和建议。


扩展

线程是什么?
  1. 线程是系统调度的最小单元,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等。会和进程内其他线程共享文件描述符、虚拟地址空间等。

  2. 分为内核线程、用户线程。Java 1.2 后,抛弃用户线程。现在模型是一对一映射到操作系统内核线程

线程实现
  1. 参考:Java 八股/03-Java 并发/创建线程有几种方式?

线程状态
  1. 状态和方法的转换图

    • img

线程使用
  1. 守护线程(Daemon Thread)

    • 必须是线程启动之前设置

    •    
       Thread daemonThread = new Thread();
       daemonThread.setDaemon(true);
       daemonThread.start();
    • 参考: 用户线程与守护线程

  2. 线程等待条件推荐写法(解决 “虚假唤醒” 问题)

    •    
       // 推荐
       while ( isCondition()) {
       waitForAConfition(...);
       }
       ​
       // 不推荐,可能引入bug
       if ( isCondition()) {
       waitForAConfition(...);
       }
       ​
  3. 慎用 ThreadLocal

    • ThreadLocal 是弱引用,回收依赖于显式触发,否则就等待线程结束。这就很容易 OOM。所以,推荐应用一定要自己负责 remove。

    • 不要和线程池配合,因为 worker 线程往往是不会自己退出。


分享: Java 纤程库 - Quasar

内容引自

Thread vs Quasar

虽然Java的线程的API封装的很好,使用起来非常的方便,但是使用起来也得小心。首先线程需要耗费资源,所以单个的机器上创建上万个线程很困难,其次线程之间的切换也需要耗费CPU,在线程非常多的情况下导致很多CPU资源耗费在线程切换上,通过提高线程数来提高系统的性能有时候适得其反。你可以看到现在一些优秀的框架如Netty都不会创建很多的线程,默认2倍的CPU core的线程数就已经应付的很好了,比如node.js可以使用单一的进程/线程应付高并发。

纤程使用的资源更少,它主要保存栈信息,所以一个系统中可以创建上万的纤程Fiber,而实际的纤程调度器只需要几个Java线程即可。

如果使用线程,执行完1万个操作需要50秒,平均延迟为1秒左右(我们故意让延迟至少1秒),线程池数量为200。(其实总时间50秒可以计算出来)。但是如果使用纤程,执行完1万个操作仅需要1.158秒,平均延迟时间为1秒,线程数量为CPU core数(缺省使用ForkJoinPool)。

可以看到,通过使用纤程,尽受限于系统的业务逻辑,我们没有办法提升业务的处理时间, 但是我们确可以极大的提高系统的吞吐率,如上面的简单的例子将10000个操作的处理时间从50秒提高到1秒,非凡的成就。

Project Loom

Project Loom ( https://openjdk.java.net/projects/loom/ )。这个项目在18年底的时候已经达到可初步演示的原型阶段。不同于之前的方案,Project Loom 是从 JVM 层面对多线程技术进行彻底的改变。Project Loom 是从 JVM 层面对多线程技术进行彻底的改变。

设计思想与之前的一个开源 Java 协程技术非常相似。这个技术就是 Quasar Fiber Parallel Universe 。而现在 Project Loom 的主要设计开发人员 Ron Pressler 就是来自 Quasar Fiber。

这里建议大家读一下 Project Loom 的这篇文档:Loom Proposal.md。这篇文档介绍了发起 Project Loom 的原因,以及 Java 线程基础的很多底层设计。

在引入 Project Loom 之后,JDK 将引入一个新类:java.lang.Fiber。此类与 java.lang.Thread 一起,都成为了 java.lang.Strand 的子类。即线程变成了一个虚拟的概念,有两种实现方法:Fiber 所表示的轻量线程和 Thread 所表示的传统的重量级线程。

参考地址:Java核心技术面试精讲_Java面试_Java基础-极客时间

↑↑↑ 欢迎 点赞、关注、收藏!!!,10 年 IT 行业老鸟,持续分享更多 IT 干货

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写文章的大米

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值