【JavaEE初阶】Thread 类及常见方法、线程的状态
目录
1、Thread 类及常见方法
1.1 Thread 的常见构造方法
1.2 Thread 的几个常见属性
1.3 启动⼀个线程 - start()
1.4 中断⼀个线程
1.5 等待⼀个线程 - join()
1.6 获取当前线程引用
1.7 休眠当前线程
2、线程的状态
2.1 观察线程的所有状态
2.2 线程状态和状态转移的意义
2.3 观察线程的状态和转移
1、Thread 类及常见方法
Thread 类是 JVM 用来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。

1.1 Thread 的常见构造方法

代码示例:
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
1.2 Thread 的几个常见属性

- ID 是线程的唯⼀标识,不同线程不会重复,是JVM自动分配的身份标识
- 名称是各种调试工具用到
- 状态表示线程当前所处的⼀个情况,下面我们会进⼀步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
补充:
前台线程的运行会阻止进程的结束;后台线程的运行不会阻止进程的结束。
咱们代码创建的线程,默认就是前台线程,会阻止进程的结束,只要前台线程没执行完,进程就不会结束,即使main已经执行完毕了。
代码举例:
public class ThreadDemo {public static void main(String[] args) {Thread t= new Thread(()->{for (int i = 0;i < 10;i++) {System.out.println("线程工作");try{Thread.sleep(3000);}catch (InterruptedException e) {e.printStackTrace();}}});t.start();} }分析这个代码,当执行到t.start()时,会创建一个新的线程,新的线程去执行循环,而main线程继续自己的后续代码的执行,此时后面已没有代码,则main线程执行完毕,可以通过jonsole工具进行查看,如图:
按照我们之前的理解,main执行完毕,进程就应该结束,但是很明显,该进程依然继续执行,我们可以根据上述代码的运行结果来看:
若我们把 t 线程设为后台线程,结果又是如何呢?
设为后台线程的代码:
public class ThreadDemo {public static void main(String[] args) {Thread t= new Thread(()->{for (int i = 0;i < 10;i++) {System.out.println("线程工作");try{Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}});t.setDaemon(true);t.start();} }这时的运行结果如图,可知当线程 t 设为后台线程就不会阻止进程的结束了,当main执行完毕,进程就直接结束了。
- 是否存活,即简单的理解为 run 方法是否运行结束了
补充:
isAlive()该方法表示了内核中的线程(PCB)是否还存在。
java代码中定义的线程对象(Thread)实例,虽然表示一个线程,但这个对象本身的生命周期与内核中的线程PCB生命周期是不完全一样的。
- 当实例化完一个对象 t 时,此时 t 对象有了,但内核pcb还没有创建,所以此时isAlive()是false的。
- 当执行完 t.start() ,这时就真正在内核中创建出pcb,此时isAlive()的值就是true了。
- 当线程run执行完了,此时内核中的线程就结束了(内核pcb就释放了),但是t对象可能还存在,isAlive()的值仍是false。
- 线程的中断问题,下面我们进⼀步说明
下面是代码示例:
public class ThreadDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + ": 我还活着");Thread.sleep(1 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我即将死去");});System.out.println(Thread.currentThread().getName()+ ": ID: " + thread.getId());System.out.println(Thread.currentThread().getName()+ ": 名称: " + thread.getName());System.out.println(Thread.currentThread().getName()+ ": 状态: " + thread.getState());System.out.println(Thread.currentThread().getName()+ ": 优先级: " + thread.getPriority());System.out.println(Thread.currentThread().getName()+ ": 后台线程: " + thread.isDaemon());System.out.println(Thread.currentThread().getName()+ ": 活着: " + thread.isAlive());System.out.println(Thread.currentThread().getName()+ ": 被中断: " + thread.isInterrupted());thread.start();while (thread.isAlive()) {}System.out.println(Thread.currentThread().getName()+ ": 状态: " + thread.getState());}
}
运行结果:

1.3 启动⼀个线程 - start()
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start()方法,就是喊⼀声:“行动起来!”,线程才真正独立去执行了

调用 start 方法, 才真的在操作系统的底层创建出⼀个线程. 对于同一个Thread对象来说,start只能调用一次。
经典面试题:start 和 run 的区别
用一个代码来说明:
class MyThread8 extends Thread {@Overridepublic void run() {while (true) {System.out.println("hello");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}} } public class ThreadDemo8 {public static void main(String[] args) {Thread t = new MyThread8();//t.run(); //此时是main方法调用run,没有创建新线程,后续的循环执行不到t.start(); //会创建新的线程,新线程执行run的循环,主线程main继续后续代码while (true) {System.out.println("hello main");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}} }当创建一个Thread类对象 t 时:
由 t 调用run方法时( t.run() ),并没有创建出一个新的线程,这个操作还是在主线程main中进行的,循环打印hello,此时代码就只能停留在run的循环中了,下方main中的循环执行不到。
若由 t 调用start,这时会创建出一个新的线程,去执行run循环;main线程则继续执行自己的后续循环。
总结:
作用功能不同:
- run方法的作用是描述线程具体要执行的任务;
- start方法的作用是真正的去申请系统线程
运行结果不同:
- run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
- start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。
1.4 中断⼀个线程
接着上面图片张三、李四的例子,李四⼀旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停⽌转账,那张三该如何通知李四停止呢?这就涉及到我们的停⽌线程的方式了。
- 通过共享的标记来进行沟通
- 调用 interrupt() 方法来通知
示例1: 使用自定义的变量来作为标志位,示例代码:
public class ThreadDemo9 {private static boolean isQuit = false;public static void main(String[] args) {Thread t = new Thread(()->{while (!isQuit) {System.out.println("hello");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}System.out.println("t结束");});t.start();try {Thread.sleep(3000);}catch (InterruptedException e) {e.printStackTrace();}System.out.println("让t线程结束");isQuit = true;}
}
运行结果:

我们可以看到代码中,将自定义的变量标志位写成了类的静态成员变量,那是否可以写为main方法中的局部变量?
不可以! 当你定义为局部变量时,会发现提示编译错误。
这是因为我们使用了创建线程的lambda表达式方法,lambda表达式有一个语法:变量捕获。
lambda表达式的变量捕获,本质上就是,把外面的变量当作参数传进来(参数是隐藏的)。这个捕获的变量得是 final 修饰的或者“事实final”(虽然没有写final,但是没有修改)。因为此处isQuit是确实要修改的,不能写成final,它也不是事实final,因此将标志位变量isQuit定义为局部变量是行不通的!
那为什么可以定义为成员变量呢?
lambda表达式,本质上是“函数式接口”,匿名内部类。对于内部类,访问外部类的成员是完全可以的,这个事情不受到变量捕获的影响。
Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记。

示例代码:
public static void main(String[] args) {Thread t = new Thread(()->{while (!Thread.currentThread().isInterrupted()) {System.out.println("我是一个线程,正在工作");//interrupt会影响sleep,线程不会结束try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程结束");});t.start();try {Thread.sleep(3000);}catch (InterruptedException e) {e.printStackTrace();}System.out.println("让t线程结束");t.interrupt();}
}
运行结果:

可以看到抛出异常,并且线程不会结束,继续往下循环输出。这是因为什么呢?
这是因为存在sleep,在执行sleep的过程中,调用interrupt。大概率sleep休眠时间还没到,被提前唤醒了。
sleep被提前唤醒,会做两件事:
- 抛出InterruptedException,紧接着就会被catch捕获到
- 清除Thread对象的isInterrupted标志位
对于线程不会结束,就是标志位被清除了。我们通过interrupt方法,已经把标志位设为true了,但是sleep提前唤醒操作,又把标志位清除,设为原来的false,所以线程不会结束。
如何解决呢?
要想线程结束,只需要在catch中加上break即可。但此时仍会抛异常,不想抛,就不写输出e.printStackTrace()。
Thread t = new Thread(()->{while (!Thread.currentThread().isInterrupted()) {System.out.println("我是一个线程,正在工作");try {Thread.sleep(1000);}catch (InterruptedException e) {// e.printStackTrace();break; //加上break,仍会抛异常(不想抛,就不写上面输出e.printStackTrace()),线程会结束}}System.out.println("线程执行完毕");});
这时的结果:

thread 收到通知的方式有两种:
- 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,并清除中断标志。当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法,可以选择忽略这个异常,也可以跳出循环结束线程。
- 否则,只是内部的⼀个中断标志被设置,thread 可以通过 Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志。这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
1.5 等待⼀个线程 - join()
代码示例:
public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Runnable target = () -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + ": 我还在⼯作!");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我结束了!")};Thread thread1 = new Thread(target, "李四");Thread thread2 = new Thread(target, "王五");System.out.println("先让李四开始⼯作");thread1.start();thread1.join();System.out.println("李四⼯作结束了,让王五开始⼯作");thread2.start();thread2.join();System.out.println("王五⼯作结束了");}
}
这里是几个join方法:

1.6 获取当前线程引用

public class ThreadDemo {public static void main(String[] args) {Thread thread = Thread.currentThread();System.out.println(thread.getName());}
}
1.7 休眠当前线程

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(3 * 1000);System.out.println(System.currentTimeMillis());}
}
2、线程的状态
2.1 观察线程的所有状态
线程的状态是⼀个枚举类型 Thread.State.
public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}
- NEW: 安排了工作, 还未开始行动
- RUNNABLE: 可工作的,又可以分成正在⼯作中和即将开始⼯作.
- BLOCKED: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- TIMED_WAITING: 这几个都表示排队等着其他事情
- TERMINATED: 工作完成了.
2.2 线程状态和状态转移的意义

大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。

2.3 观察线程的状态和转移
观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换
public class ThreadStateTransfer {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0; i < 1000_0000; i++) {}}, "李四");System.out.println(t.getName() + ": " + t.getState());;t.start();while (t.isAlive()) {System.out.println(t.getName() + ": " + t.getState());;}System.out.println(t.getName() + ": " + t.getState());;}
}
观察 2: 关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
public static void main(String[] args) {final Object object = new Object();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object) {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}, "t1");t1.start();Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object) {System.out.println("hehe");}}}, "t2");t2.start();
}
public static void main(String[] args) {final Object object = new Object();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object) {try {// [修改这⾥就可以了!!!!!]// Thread.sleep(1000);object.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}, "t1");...}
使用户jconsole 可以看到 t1 的状态是 WAITING。
- BLOCKED 表示等待获取锁,WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.
- TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒
相关文章:
【JavaEE初阶】Thread 类及常见方法、线程的状态
目录 1、Thread 类及常见方法 1.1 Thread 的常见构造方法 1.2 Thread 的几个常见属性 1.3 启动⼀个线程 - start() 1.4 中断⼀个线程 1.5 等待⼀个线程 - join() 1.6 获取当前线程引用 1.7 休眠当前线程 2、线程的状态 2.1 观察线程的所有状态 2.2 线程状态和状…...
0 NLP: 数据获取与EDA
0数据准备与分析 二分类任务,正负样本共计6W; 数据集下载 https://github.com/SophonPlus/ChineseNlpCorpus/raw/master/datasets/online_shopping_10_cats/online_shopping_10_cats.zip 样本的分布 正负样本中评论字段的长度 ,超过500的都…...
159.库存管理(TOPk问题!)
思路:也是tok的问题,与上篇博客思路一样,只不过是求前k个小的元素! 基于快排分块思路的代码如下: class Solution { public:int getkey(vector<int>&nums,int left,int right){int rrand();return nums[r%…...
【开源】基于Vue+SpringBoot的康复中心管理系统
项目编号: S 056 ,文末获取源码。 \color{red}{项目编号:S056,文末获取源码。} 项目编号:S056,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员…...
设计模式总览
一、设计模式 介绍 种一棵树最好的时间是十年前,其次是现在 《援助的死亡》-- 比萨莫约 The best time to plant a tree was 10 years ago。 The second best time is now。 《dead aid》-- Dambisa Moyo 1、创建型模式 1.1、单例模式 确保一个类最多只有一个实…...
数据链路层之VLAN基本概念和基本原理
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…...
UVA11729 Commando War
UVA11729 Commando War 题面翻译 突击战 你有n个部下,每个部下需要完成一项任务。第i个部下需要你花Bj分钟交代任务,然后他就会立刻独立地、无间断地执行Ji分钟后完成任务。你需要选择交代任务的顺序,使得所有任务尽早执行完毕(…...
【数据库】数据库基于封锁机制的调度器,使冲突可串行化,保障事务和调度一致性
封锁使可串行化 专栏内容: 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会定期更…...
大文件分片上传、分片进度以及整体进度、断点续传(一)
大文件分片上传 效果展示 前端 思路 前端的思路:将大文件切分成多个小文件,然后并发给后端。 页面构建 先在页面上写几个组件用来获取文件。 <body><input type"file" id"file" /><button id"uploadButton…...
Pytest 的小例子
一个简单的例子 下面代码保存到test_pytest.py 一个简单的例子 def inc(x):return x 1def test_answer():assert inc(3) 5def test_ask():assert inc(4) 5 pytest 需要安装一下 pip install pytest (Venv) D:\pythonwork>pip install pytest Collecting pytestDown…...
大数据(十一):概率统计基础
专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础)》 再推荐一下最近热更的:《大厂测试高频面试题详解》 该专栏对…...
web前端之TypeScript
MENU typescript类型别名、限制值的大小typescript使用class关键字定义一个类、static、readonlytypescript中class的constructor(构造函数)typescript中abstractClass(抽象类)、extends、abstracttypescript中的接口、type、interfacetypescript封装属性、public、private、pr…...
计网Lesson6 - IP 地址分类管理
文章目录 1. I P IP IP 地址定义2. I P v 4 IPv4 IPv4 的表示方法2.1 I P v 4 IPv4 IPv4 的分类编址法2.2 I P v 4 IPv4 IPv4 的划分子网法2.2.1 如何划分子网2.2.2 如何确定子网的借位数2.2.3 总结2.2.4 题目练习 2.3 I P v 4 IPv4 IPv4 的无分类编址法 1. I P IP IP 地…...
Nat. Mach. Intell. | 预测人工智能的未来:在指数级增长的知识网络中使用基于机器学习的链接预测
今天为大家介绍的是来自Mario Krenn团队的一篇论文。一个能够通过从科学文献中获取洞见来建议新的个性化研究方向和想法的工具,可以加速科学的进步。一个可能受益于这种工具的领域是人工智能(AI)研究,近年来科学出版物的数量呈指数…...
MySQL海量数据配置优化教程
1.缓存大小调整 缓存是数据库中用于减少磁盘 I/O 操作的重要机制。通过增加缓存大小,可以减少对磁盘的访问,从而提高查询性能。 可以使用 innodb_buffer_pool_size 参数来调整 InnoDB 缓存的大小。例如,将缓存大小设置为服务器内存的 70% my…...
Mac-idea快捷键操作
–以下是程序员在Mac中常用的快捷键 弹出程序坞ctrol f3 窗口满屏,半屏 ctrol command f 切换同一个程序的窗口 command ~ 打开最小化窗口 command tab option 拷文件路径 command option c 显示隐藏文件command shift . 显示所有窗口 control 向上箭头 chrome 全屏…...
HarmonyOS脚手架:UI组件之文本和图片
前言 关于HarmonyOS脚手架,本篇是系列的第二篇,主要实现UI组件文本和图片的常见效果查看,本身功能特别的简单,其目的也是很明确,方便大家根据效果查看相关代码实现,可以很方便的进行复制使用,当…...
详细学习Pyqt5中的6种按钮
Pyqt5相关文章: 快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图(Item View) 快速弄懂Pyqt5的4种项目部件(Item Widget) 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的10种容器&…...
【工具】Zotero|使用Zotero向Word中插入引用文献(2023年)
版本:Word 2021,Zotero 6.0.30 前言:两年前我找网上插入文献的方式,网上的博客提示让我去官网下个插件然后才能装,非常麻烦,导致我对Zotero都产生了阴影。最近误打误撞发现Zotero自带了Word插件,…...
利用Python爬虫爬取豆瓣电影排名信息
可以使用第三方库Beautiful Soup和Requests来编写一个简单的爬虫,从豆瓣电影Top100页面获取信息 import requests from bs4 import BeautifulSoupdef get_douban_top100():url https://movie.douban.com/top250headers {User-Agent: Mozilla/5.0 (Windows NT 10.…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...



