面试官:线程调用2次start会怎样?我支支吾吾没答上来
写在开头
在写完上一篇文章《Java面试必考题之线程的生命周期,结合源码,透彻讲解!》后,本以为这个小知识点就总结完了。
但刚刚吃晚饭时,突然想到了多年前自己面试时的亲身经历,决定再回来补充一个小知识点!
记得是一个周末去面试Java后端开发工程师岗位,面试官针对Java多线程进行了狂轰乱炸般的考问,什么线程创建的方式、线程的状态、各状态间的切换、如果保证线程安全、各种锁的区别,如何使用等等,因为有好好背八股文,所以七七八八的也答上来了,但最后面试官问了一个现在看来很简单,但当时根本不知道的问题,他先是问了我,看过Thread的源码没,我毫不犹豫的回答看过,紧接着他问:
线程在调用了一次start启动后,再调用一次可以不?如果线程执行完,同样再调用一次start又会怎么样?
这个问题抛给你们,请问该如何作答呢?
线程的启动
我们知道虽然很多八股文面试题中说Java创建线程的方式有3种、4种,或者更多种,但实际上真正可以创建一个线程的只有new Thread().start();
【代码示例1】
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(() -> {});
System.out.println(thread.getName()+":"+thread.getState());
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
输出:
Thread-0:NEW
Thread-0:RUNNABLE
创建一个Thread,这时线程处于NEW状态,这时调用start()方法,会让线程进入到RUNNABLE状态。
RUNNABLE的线程调用start
在上面测试代码的基础上,我们再次调用start()方法。
【代码示例2】
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(() -> {});
System.out.println(thread.getName()+":"+thread.getState());
//第一次调用start
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
//第二次调用start
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
输出:
Thread-0:NEW
Thread-0:RUNNABLE
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at com.javabuild.server.pojo.Test.main(Test.java:17)
第二次调用时,代码抛出IllegalThreadStateException异常。
这是为什么呢?我们跟进start源码中一探究竟!
【源码解析1】
// 使用synchronized关键字保证这个方法是线程安全的
public synchronized void start() {
// threadStatus != 0 表示这个线程已经被启动过或已经结束了
// 如果试图再次启动这个线程,就会抛出IllegalThreadStateException异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 将这个线程添加到当前线程的线程组中
group.add(this);
// 声明一个变量,用于记录线程是否启动成功
boolean started = false;
try {
// 使用native方法启动这个线程
start0();
// 如果没有抛出异常,那么started被设为true,表示线程启动成功
started = true;
} finally {
// 在finally语句块中,无论try语句块中的代码是否抛出异常,都会执行
try {
// 如果线程没有启动成功,就从线程组中移除这个线程
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 如果在移除线程的过程中发生了异常,我们选择忽略这个异常
}
}
}
这里有个threadStatus,若它不等于0表示线程已经启动或结束,直接抛IllegalThreadStateException异常,我们在start源码中打上断点,从第一次start中跟入进去,发现此时没有报异常。

此时的threadStatus=0,线程状态为NEW,断点继续向下走时,走到native方法start0()时,threadStatus=5,线程状态为RUNNABLE。此时,我们从第二个start中进入断点。

这时threadStatus=5,满足不等于0条件,抛出IllegalThreadStateException异常!
TERMINATED的线程调用start
终止状态下的线程,情况和RUNNABLE类似!
【代码示例3】
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {});
thread.start();
Thread.sleep(1000);
System.out.println(thread.getName()+":"+thread.getState());
thread.start();
System.out.println(thread.getName()+":"+thread.getState());
}
}
输出:
Thread-0:TERMINATED
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at com.javabuild.server.pojo.Test.main(Test.java:17)
这时同样也满足不等于0条件,抛出IllegalThreadStateException异常!
我们其实可以跟入到state的源码中,看一看线程几种状态设定的逻辑。
【源码解析2】
// Thread.getState方法源码:
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
// sun.misc.VM 源码:
// 如果线程的状态值和4做位与操作结果不为0,线程处于RUNNABLE状态。
// 如果线程的状态值和1024做位与操作结果不为0,线程处于BLOCKED状态。
// 如果线程的状态值和16做位与操作结果不为0,线程处于WAITING状态。
// 如果线程的状态值和32做位与操作结果不为0,线程处于TIMED_WAITING状态。
// 如果线程的状态值和2做位与操作结果不为0,线程处于TERMINATED状态。
// 最后,如果线程的状态值和1做位与操作结果为0,线程处于NEW状态,否则线程处于RUNNABLE状态。
public static State toThreadState(int var0) {
if ((var0 & 4) != 0) {
return State.RUNNABLE;
} else if ((var0 & 1024) != 0) {
return State.BLOCKED;
} else if ((var0 & 16) != 0) {
return State.WAITING;
} else if ((var0 & 32) != 0) {
return State.TIMED_WAITING;
} else if ((var0 & 2) != 0) {
return State.TERMINATED;
} else {
return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
}
}
总结
OK,今天就讲这么多啦,其实现在回头看看,这仅是一个简单且微小的细节而已,但对于刚准备步入职场的我来说,却是一个难题,今天写出来,除了和大家分享一下Java线程中的小细节外,更多的是希望正在准备面试的小伙伴们,能够心细,多看源码,多问自己为什么?并去追寻答案,Java开发不可浅尝辄止。
结尾彩蛋
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

面试官:线程调用2次start会怎样?我支支吾吾没答上来的更多相关文章
- 面试一个百度T7程序员,一道简单的题没答上来!网友却都在吐槽面试官!
程序员面试时都考些什么? 一个面试官得意洋洋地说自己面了一个百度T7,出了一道coding题,结果对方连最长上升子序列都写不出来. 楼主本想嘲弄一下百度T7的代码水平低,没想到网友们炸开了锅,纷纷 ...
- 面试官:Java 线程如何启动的?
摘要:Java 的线程创建和启动非常简单,但如果问一个线程是怎么启动起来的往往并不清楚,甚至不知道为什么启动时是调用start(),而不是调用run()方法呢? 本文分享自华为云社区<Threa ...
- 面试官没想到一个Volatile,我都能跟他扯半小时
点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...
- 关于Qt跨线程调用IO子类的理解
一.疑问 突然想到,类似于QTcpsocket和QSerialport这类对象,如果是在A线程中new的,那就不能在其他线程中访问.我一般是这样做的: 封装一个QObject子类,放这些对象进去,然后 ...
- 攻略前端面试官(一):JS的数据类型和内存机制浅析
原文地址:http://rainykane.cn/2019/09/29/与K_K君一起攻略前端面试官(一):JS的数据类型和内存机制浅析/ 背就完事了 介绍:一些知识点相关的面试题和答案 使用姿势:看 ...
- 面试官:ElasticSearch是什么,它有什么特性与使用场景?
哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,我本以为我跟面试我的 ...
- 图解Java线程的生命周期,看完再也不怕面试官问了
文章首发自个人微信公众号: 小哈学Java https://www.exception.site/java-concurrency/java-concurrency-thread-life-cycle ...
- 面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?
摘要: 原创出处 https://studyidea.cn 「公众号:程序通事 」欢迎关注和转载,保留摘要,谢谢! 使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而 ...
- 面试官:线程池如何按照core、max、queue的执行循序去执行?(内附详细解析)
前言 这是一个真实的面试题. 前几天一个朋友在群里分享了他刚刚面试候选者时问的问题:"线程池如何按照core.max.queue的执行循序去执行?". 我们都知道线程池中代码执行顺 ...
- 面试官:Netty的线程模型可不只是主从多Reactor这么简单
笔者看来Netty的内核主要包括如下图三个部分: 其各个核心模块主要的职责如下: 内存管理 主要提高高效的内存管理,包含内存分配,内存回收. 网通通道 复制网络通信,例如实现对NIO.OIO等底层JA ...
随机推荐
- CentOS使用iptables开放3000端口
关闭firewall systemctl stop firewalld.service 禁止firewall开机启动 systemctl disable firewalld.service 设置ipt ...
- 【主流技术】聊一聊对 Mybatis Plus 的理解与应用
前言 mybatis plus是一个mybatis的增强工具,在其基础上只做增强不做改变.作为开发中常见的第三方组件,学习并应用在项目中可以节省开发时间,提高开发效率. 官方文档地址:MyBatis- ...
- 解决:VScode中 import 后出现no module的问题
问题: ModuleNotFoundError: No module named 'xxx' 除去没有安装包的问题 这个问题还是挺难受的,pycharm和终端都可以运行,只有vscode报错 方法一: ...
- Flask 框架:运用WTForms实现用户注册
WTForms 是用于web开发的灵活的表单验证和呈现库,它可以与您选择的任何web框架和模板引擎一起工作,并支持数据验证.CSRF保护.国际化等,运用WTForms框架并配合Flask可实现一个带有 ...
- C/C++ 常用排序算法整理
(伪)冒泡排序算法: 相邻的两个元素之间,如果反序则交换数值,直到没有反序的记录为止. #include <stdio.h> void BubbleSort(int Array[], in ...
- 我自创的 Response泛型 返回类,全自动 推断,非常方便使用。
package com.diandaxia.common.utils; import java.util.Date; /** * by liyuxin 2019.12.16 更新 * 统一返回格式 * ...
- IIS的详细配置
一:配置默认文档 输入ip打开哪个页面是由默认文档设定的 1.打开IIS配置页面,点击网站.我们的默认站点已经启动,可以看到绑定的ip和网页的路径 2.选中Default Web Site,可以看到有 ...
- 2024年,我又开始用Linux桌面作为主力系统了~
前言 19年的时候我买了一个 matebook14 笔记本,配置是8代i5和8G内存,在当时看来作为轻薄本是够用的,但是现在已经2024年了,这内存让我想起来去年苹果的新款 mac mini ,丐版三 ...
- NC204418 新集合
题目链接 题目 题目描述 集合 \(s\) 中有整数 \(1\) 到 \(n\) ,牛牛想从中挑几个整数组成一个新的集合. 现在牛妹给牛牛加了 \(m\) 个限制 ,每个限制包含两个整数 \(u\) ...
- UVA11573 Ocean Currents
题目链接 题目 见链接. 题解 知识点:BFS. 这道题显然用BFS,但发现洋流方向会破坏时间的有序性,但注意到洋流时间花费是 \(0\) ,因此只需要用双端队列即可,洋流方向扩展直接放队头,其他方向 ...