↑↑↑ 欢迎 点赞、关注、收藏!!!,10 年 IT 行业老鸟,持续分享更多 IT 干货
注: 本笔记为 公司内部技术小组持续学习 2 年多时间 + 个人整理不下 5 次的结果产出。
目录
17 | 线程的生命周期和状态转移
每日一问
一个线程两次 start() 方法会出现什么情况?谈谈线程的生命周期和状态转移?
回答
-
Java 的线程是不允许启动两次的,第二次必然会抛出
IllegalThreadStateException
。这是一种运行时异常,多次调用 start 被认为是编程错误。 -
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)
-
线程终止运行。
-
-
分析
-
线程是什么?及 Java 底层实现方式。
-
线程状态切换,及和锁等并发工具类的互动。
-
线程编程时容易踩的坑和建议。
扩展
线程是什么?
-
线程是系统调度的最小单元,有自己的栈(Stack)、寄存器(Register)、本地存储(Thread Local)等。会和进程内其他线程共享文件描述符、虚拟地址空间等。
-
分为内核线程、用户线程。Java 1.2 后,抛弃用户线程。现在模型是一对一映射到操作系统内核线程。
线程实现
线程状态
-
状态和方法的转换图
-
线程使用
-
守护线程(Daemon Thread)
-
必须是线程启动之前设置。
-
Thread daemonThread = new Thread(); daemonThread.setDaemon(true); daemonThread.start();
-
参考: 用户线程与守护线程
-
-
线程等待条件推荐写法(解决 “虚假唤醒” 问题)
-
// 推荐 while ( isCondition()) { waitForAConfition(...); } // 不推荐,可能引入bug if ( isCondition()) { waitForAConfition(...); }
-
-
慎用 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 干货