Thread类的属性及常见方法

Thread是JVM用于管理线程的类,换句话说,每个线程都有一个Thread对象与之关联,一个Thread对象有ID、名称、优先级、状态等属性,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。

1. Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnable对象创建线程对象,并命名

给Thread命名的意义:上一篇文章中我们使用jconsole查看过自定义的线程,由于没有命名,它的默认名称为Thread-0,我们可以在构造函数中给线程命名(与内核中的线程一一对应),以便后续调试。

public class Demo7 {public static void main(String[] args) {Thread thread = new Thread(()->{while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}, "myThread");thread.start();while (true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

打开jconsole再次查看:

image.png

2. Thread的常见属性

属性获取方法
ID:线程在JVM中的唯一标识符getId()
名称getName()
状态getState()
优先级getPriority()
是否为守护线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

守护线程的解释:创建出来的线程默认为非守护线程,守护线程不会阻止进程结束,非守护线程会阻止进程结束。也就是说所有非守护线程必须执行完了进程才会结束,如果想设置线程为守护线程可以使用setDaemon(true)方法来设置线程为守护线程。

使用一下上面的api,查看Thread的属性:

public class Demo8 {public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}, "myThread");t.start();System.out.println(t.getId());System.out.println(t.getName());System.out.println(t.getState());System.out.println(t.getPriority());System.out.println(t.isDaemon());System.out.println(t.isAlive());System.out.println(t.isInterrupted());}
}

运行结果:下面都是线程一瞬间的属性

11
myThread
TIMED_WAITING
5
false
true
false

3. 启动一个线程——start()

创建出Thread实例,只是在JVM的堆区中创建出了一个Thread对象,并没有在操作系统中创建出一个线程,调用了start()方法才是在操作系统中创建出一个线程并完成指定的入口方法(重写的run()方法或者Runnable对象),并且当线程的入口方法完成后,该线程就随之结束了。

4. 获取线程引用——currentThread()

Thread类有一个静态方法currentThread(),用于在入口方法(线程需要做的事)内部获取到线程的引用。

  • 如果是继承Thread类,重写run方法,可以直接在run方法内使用this获取到线程引用
  • 但如果是Runnable内重写run方法,this就不管用了,需要使用Thread.currentThread()方法获取引用

5. 中断线程——interrupt()

所谓的中断线程,就是让线程尽快把入口方法执行结束,Java中把中断的决定权交给被中断的线程本身,接下来我将以两种方式举例如何中断线程。

  1. 直接使用标识为来区分线程是否需要中断
public class Demo9 {private static boolean isQuit = false;public static void main(String[] args) {Thread thread = new Thread(() -> {while (!isQuit) {System.out.println("thread is running..");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("thread is finished! ");});thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}//主线程休眠五秒后将标志位设为trueisQuit = true;}
}
thread is running..
thread is running..
thread is running..
thread is running..
thread is running..
thread is finished! Process finished with exit code 0

这种方案是可行的,Thread其实内置了标志位,不需要我们手动创建标志位

  1. 使用Thread自带的标志位来判断是否要中断

设置标志位的方法如下:

//调用后标志位就设为true了
thread.interrupt();

判断标志位的方法如下:

//普通成员方法,中断后不清除中断标志(还是true)
Thread.currentThread().isInterrupted()
//静态成员方法,中断后清除中断标志(变为false)
Thread.interrupted()

使用这两个方法来中断线程:

public class Demo10 {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("thread is running..");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}//主线程休眠五秒后中断线程System.out.println("控制线程退出");thread.interrupt();}
}

此时会发生一个问题,运行结果如下:

thread is running..
thread is running..
thread is running..
thread is running..
thread is running..
控制线程退出
Exception in thread "Thread-0" java.lang.RuntimeException: java.lang.InterruptedException: sleep interruptedat thread.Demo10.lambda$main$0(Demo10.java:18)at java.lang.Thread.run(Thread.java:750)
Caused by: java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at thread.Demo10.lambda$main$0(Demo10.java:16)... 1 more

thread.interrupt()在线程非阻塞状态和阻塞状态下会有两种行为:

  1. 非阻塞状态:thread.interrupt()会修改内置的标志位为true
  2. 阻塞状态:thread.interrupt()方法会使如sleep()这样阻塞线程的方法出现异常,又由于我们在catch代码块中捕获到异常的处理方式是直接throw抛出异常,因此进程就会因为异常而意外中断。

由于我们写的该线程大部分时间都是处于阻塞状态,因此我们需要对catch代码块中的代码进行特殊处理:

Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("thread is running..");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}
});

此时就可以正常地中断线程了:

thread is running..
thread is running..
thread is running..
thread is running..
thread is running..
控制线程退出Process finished with exit code 0

6. 等待一个线程——join()

join()方法的行为:

  1. 如果被等待的线程还没执行完,就阻塞等待
  2. 如果等待的线程已经执行完,就直接返回

由于调度器的策略是我们无法干涉的,但是使用join()可以控制一些必要的执行顺序,这里我将举两个案例来帮助理解join()方法

上一篇文章中我们使用了下面的代码来控制main函数阻塞,等待t1和t2执行完了,main才能解除阻塞,继续往下执行:

public class Demo6 {private static final long COUNT = 10_0000_0000;private static void serial() {long begin = System.currentTimeMillis();int a = 0;for (int i = 0; i < COUNT; i++) {a++;}a = 0;for (int i = 0; i < COUNT; i++) {a++;}long end = System.currentTimeMillis();System.out.println("共花费了" + (end - begin) + "ms");}public static void main(String[] args) {concurrency();}private static void concurrency() {long begin = System.currentTimeMillis();Thread t1 = new Thread(() -> {for (int i = 0; i < COUNT; i++) {int a = 0;a++;}});Thread t2 = new Thread(() -> {for (int i = 0; i < COUNT; i++) {int a = 0;a++;}});t1.start();t2.start();try {//等待t1,t2线程结束才接触阻塞并执行后续代码t1.join();t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}long end = System.currentTimeMillis();System.out.println("共花费了" + (end - begin) + "ms");}
}

第二个场景:t1,t2,main并发运行,但是他们的结束顺序必须是t1、t2、main,此时我们同样可以使用join()控制线程结束的顺序:

public class Demo11 {public static void main(String[] args) throws InterruptedException {System.out.println("main begin");Thread t1 = new Thread(() -> {System.out.println("t1 begin");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 end");});t1.start();Thread t2 = new Thread(() -> {System.out.println("t2 begin");//等待t1结束try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t2 end");});t2.start();//等待t2结束t2.join();System.out.println("main end");}
}

上面那种写法是一直阻塞到线程结束,但是在实际开发中,更推荐使用最多阻塞多久就不阻塞了的方式(第2、3种方法):

image.png

7. 休眠线程——sleep()

sleep()方法已经在前面的演示代码中使用了很多遍了,这里我们具体谈谈操作系统中做了什么。

第一章我们在引入线程之前讲了进程的五种状态:运行态的进程在cpu上运行,就绪态的线程在就绪队列中等待调度,阻塞态的进程在阻塞队列中,不参与调度。

引入线程后调度的基本单位变成了线程,一个线程对应一个TCB,在就绪队列中的TCB参与调度器的随机调度,在阻塞队列中的TCB不参与调度器的随机调度。

  1. 在调用sleep()方法前,它们是这样的:

image.png

  1. 当调用到sleep()后,该线程立刻就从运行态变为了阻塞态,并且将TCB放入阻塞队列中,并且CPU立马从就绪队列中调度一个新的线程运行:

image.png

  1. 当sleep的时间结束时,会唤醒该线程,该线程才从阻塞态变为就绪态,并且被放入就绪队列中等待调度器随机调度:

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/688919.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Meilisearch使用过程趟过的坑

Elasticsearch 做为老牌搜索引擎&#xff0c;功能基本满足&#xff0c;但复杂&#xff0c;重量级&#xff0c;适合大数据量。 MeiliSearch 设计目标针对数据在 500GB 左右的搜索需求&#xff0c;极快&#xff0c;单文件&#xff0c;超轻量。 所以&#xff0c;对于中小型项目来说…

HashSet扩容机制

HashSet底层是HashMap,第一次添加的时候,table数组扩容到16,临界值是16*加载因子(默认是0.75),到达临界值进行扩容。 HashSet<Integer> hashSet = new HashSet<>();hashSet.add(5);hashSet.add(2);hashSet.add(5);hashSet.add(8);hashSet.add(1);当new一个H…

地磁暴红色预警来袭,普通人该如何应对?绝绝子的防护指南来了

近日&#xff0c;国家空间天气监测预警中心发布了一则令人瞩目的消息——地磁暴红色预警。这一预警不仅提醒我们地磁暴即将影响我国的电离层和低轨卫星&#xff0c;更让我们深刻认识到地球空间环境的脆弱性和复杂性。对于普通公众而言&#xff0c;地磁暴的概念可能相对陌生&…

OpenNJet,够轻更强云原生应用引擎

前言&#xff1a; 在正式介绍OpenNJet之前&#xff0c;我们先来看看它的技术架构&#xff0c;如下图所示&#xff0c;OpenNJet正是NGINX的Pro版&#xff0c;在100%兼容NGINX基础上&#xff0c;新增了动态配置加载、主动式健康检测、集群高可用、声明式API等多种强大功能。 NGIN…

鸿蒙DevEco Studio 4.1 Release-模拟器启动方式错误

软件版本&#xff1a;DevEco Studio 4.1 Release 报错提示&#xff1a; 没有权限查看处理指导 Size on Disk 显示1.0MB 尝试方案&#xff08;统统无效&#xff09;&#xff1a; 1、“windows虚拟机监控程序平台”、"虚拟机平台"已开启 启用CPU虚拟化 2、C…

InputStream,OutputStream的用法以及相应的案例

1. 文件系统的操作&#xff1a;File类。 2. 文件内容的操作&#xff1a;Stream流。 字符流&#xff1a;IntputStream &#xff0c; OutputStream。 字节流&#xff1a;read &#xff0c; write。 InputStream&#xff0c;OutputStream InputStream和OutputStream都不能被实例…

Acwing2024蓝桥杯FloodFill

AcWing 687. 扫雷 模拟以下样例(10X10): 把扫雷地图转变为数字记录的地图:地雷记作-1,其余表示8个方向有几个地雷,完成后如下图: 接着搜索所有0联通块(为红色矩形),并且把联通块附近不是地雷的点(红色圆形)全标记为-1,如下图: 而答案就是当前该图中大于0的数的数目之和,再加上…

深入理解JavaScript事件循环Event Loop:宏任务与微任务的奇幻之旅

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f389; 引言&#x1f31f; 什么是事件循环&#xff1f;&#x1f4da; 「宏任务」 vs 「微任务」「宏任务」(Macrotask)「微任务」(Microtask)实际应用中的注意事项 &#x1f500; 执行流程概览&#x1f4dd; 代码示例…

【数据结构】二叉树(Binary Tree)

文章目录 一、树的概念及结构二、二叉树的概念及结构1.二叉树的概念2.特殊的二叉树3.二叉树的性质 三、二叉树的存储顺序存储链式存储 四、二叉树的实现1.创建二叉树2.二叉树的遍历前序遍历中序遍历后序遍历层序遍历根据遍历顺序创建二叉树 3.二叉树的基本操作1.总结点个数2.二…

国内运营商选择爱立信,或因它的低频5G技术更先进,价格更便宜

国内某运营商将大笔5G设备订单交给爱立信&#xff0c;引发了掀然大波&#xff0c;影响仍在扩散&#xff0c;对此各方说什么原因都有&#xff0c;笔者认为爱立信此次斩获大单&#xff0c;可能在于它的低频5G设备更先进&#xff0c;价格更便宜&#xff0c;对于急于降低成本的国内…

融知财经:期货和现货的区别是什么?哪个风险大?

期货和现货在交易对象等方面存在明显的区别。期货交易是一种衍生金融工具&#xff0c;主要用于价格发现、风险管理和投机&#xff0c;而现货交易则是商品和服务的实际买卖。在选择进行期货交易还是现货交易时&#xff0c;投资者需要根据自己的需求和市场情况来决定。 期货和现货…

智能驱动,精准管理:打造高效干部管理系统

干部管理系统是现代组织管理中不可或缺的工具&#xff0c;它通过信息技术的应用&#xff0c;提高了干部管理的效率和准确性。干部管理系统的主要功能包括&#xff1a; 1. 信息管理&#xff1a;系统可以存储和管理干部的个人信息&#xff0c;包括基本资料、工作经历、教育背景、…