当前位置: 首页 > news >正文

java线程之Thread类的基本用法

Thread类的基本用法

  • 1. Thread类的构造方法
  • 2. Thread的几个常见属性
    • 常见属性
    • 线程中断
    • 等待一个线程

小鱼在上一篇博客详细的讲解了如何创建线程,java使用Thread类来创建多线程,但是对于好多没有相关经验的人来说,比较不容易理解的地方在于操作系统调度的执行过程. 我们通过下面代码举例:

    public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("hello t");});//调用start(),创建一个新的线程t.start();System.out.println("hello main");}

我们的java在启动时,会创建一个主线程,此时主线程的名字就是main,接下来我们执行main线程里面的一条条代码了,当我们执行到t.start()时,系统就会自动帮我们创建一个新的线程,并且会自动帮我们调用该线程中的run()方法,此时的run()方法就可以认为是个入口,一些重要的实现逻辑将都在里面实现.

我们通过上述代码举例:

当我们实例一个thread并且调用start()方法之后, main 线程和 t 线程就是(并发+并行)的过程,由于多线程有一个万恶之源–抢占机制,所以就会导致我们无法预测到程序是优先执行"hello t" 还是 “hello main”,并且对于谁先结束线程的任务,我们也是不确定的,可能是main线程优先结束,也可能是t线程优先结束.

在这里插入图片描述

创建线程是看start的顺序,但是具体的线程对应的任务什么时候执行,要看系统调度器。

1. Thread类的构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组

关于构造方法的讲解在这篇文章中详细的讲到了,包括如何创建一个线程,以及创建线程需要注意的事项都在这里一一列举了出来多线程的创建及其注意事项

2. Thread的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

常见属性

(1) getid() : ID表示线程的身份.就类似于我们的身份证一样,是独一无二的.这里的方法获取到的是线程在JVM中的身份标识,线程的标识有好多个,在内核的PCb上有标识,在用户态线程数据库中也有标识(操作系统的线程库)

(2) getName() : 获取当前线程的名字,多用于调试时使用.

(3) getState() :获取当前线程的状态,状态表示线程当前所属的一个情况.

(4) getPriority() : 优先级高的线程理论上来说更容易被调度到,但实际上并没有什么区别.大概可以理解为,你建议我不要晚睡,但也仅仅是建议,我可以不听.

(5) isDaemon() :daemon称为"守护线程",也可以称为后台线程,如果是后台线程就会返回true,前台线程则是false,我们可以这么理解后台线程,当我们手机打开qq这个app时,此时qq就是前台运行,但是由于我们后续可能会切换到快手app,此时qq就来到后台运行了.

一个线程创建出现默认是前台线程,前台线程会阻止进程的退出,进程只有在所有的前台线程都执行完之后才可以退出,但是对于后台进程,并不会阻止进程的结束.

JVM会在一个进程的所有非后台进程结束后,才会结束运行。

(6)isAlive() : 是否存活,即run()方法是否结束。

public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("t 线程正在运行");}});System.out.println("t线程是否存活 "+t.isAlive());t.start();System.out.println("t线程是否存活 "+t.isAlive());try {Thread.sleep(1000);System.out.println("t线程是否存活 "+t.isAlive());} catch (InterruptedException e) {e.printStackTrace();}}
}

如果 t 的run还没跑,isAlive就为false;
如果 t 的run正在跑,isAlive就为true;
如果 t 的run跑完了,isAlive就为false;

执行结果:

在这里插入图片描述

此时我们将上述涉及到的属性的状态打印出来:

在这里插入图片描述

使用setDaemon(布尔值)方法设置一个线程为后台,true表示后台线程。

class MyThread1 extends Thread{@Overridepublic void run() {System.out.println("hello  t");}
}
public class Demo1 {public static void main(String[] args) {Thread t = new MyThread1();t.start();System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//设置为后台线程t.setDaemon(true);System.out.println("是否是后台线程 "+ t.isDaemon());}
}

执行结果:

在这里插入图片描述
上述操作都是获取到的一瞬间的状态,不是持续的状态。

start()和run()方法的区别:

当我们调用start()这个方法时,创建一个新的线程(有一个向系统创建一个线程的这么一个动作),此时这个线程会由系统自动调用run()方法(入口方法).并且这个run()方法是在这个新建的线程中运行的.
但是如果是程序员手动调用run()方法的话,并不会新建一个线程,并且该方法是在调用这个方法的线程中执行的.

获取当前线程的引用:

方法说明
public static Thread currentThread();返回当前线程对象的引用
 public static void main(String[] args) {//获取当前线程的引用Thread t = Thread.currentThread();//通过当前线程的引用调用当前线程的名字System.out.println("当前线程名字 : "+t.getName());}

执行结果:
在这里插入图片描述

休眠当前线程:

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throwsInterruptedException可以更高精度的休眠
   public static void main(String[] args) {Thread t = new Thread(()->{System.out.println("hello t");});t.start();try {//睡眠1000ms,也可以称为阻塞1000msThread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello main");}

睡眠的状态就相当于这个线程被阻塞了,阻塞的时间由里面的参数决定,在这个线程阻塞的时间并不影响其他线程执行,可以理解为~我们在打游戏,打着打着未成年时间限制弹了出来,这时候就要求我们停止打游戏这个行为,只有等这个等待时间过去之后才可以继续打游戏.

:等待的过程中什么都不能做,相当于被定身了一样!!!

线程中断

中断线程,就是让线程尽快的把入口方法(run方法)执行结束。有2种方法。

(1) 直接手动创建标志位来区分线程是否要结束.

public class ThreadDemo5 {static boolean flog = true;public static void main(String[] args)  {Thread t = new Thread(()->{while (flog){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程运行中~~");}System.out.println("t 线程结束");});t.start();try {//睡眠3000msThread.sleep(3000);//将标志位设置为false终止上面的while()System.out.println("控制新线程结束");flog = false;} catch (InterruptedException e) {e.printStackTrace();}}
}

运行结果:

在这里插入图片描述
有些线程可能会存在一些循环需要执行的情况,但是可能只是需要执行一定时间,并不是一直执行,所以就可以通过sleep()来控制这个线程结束的时间.

需要注意的是:flog需要是成员变量,局部变量是线程私有的,其余线程不能进行修改,但是成员变量是处于进程的数据区,是共享的资源,所以可以访问和修改~~

Thread内置了标志位,不需要手动创建标志位。

(2)Thread内置的标志位

public class ThreadDemo5 {static boolean flog = true;public static void main(String[] args)  {Thread t = new Thread(()->{while (!Thread.currentThread().isInterrupted()){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程运行中~~");}System.out.println("t 线程结束");});t.start();try {//睡眠3000msThread.sleep(3000);//将标志位设置为false终止上面的while()System.out.println("控制新线程结束");t.interrupt();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这里插入图片描述
Thread.currentThread().这个是Thread类的静态方法,通过这个方法可以获取当前线程,类似this;

isInterrupted()方法是一个判定内置的标志位,默认是false,true表示线程要中断。现在运行,查看结果。

在这里插入图片描述

为什么在抛出异常后线程还是在不停的执行呢?

那是因为当代码执行到interrupt()时会做两件事:

(1) 如果t 线程没有处于阻塞状态时,这时候interrupt()就会修改内置标志位,将isInterrupted的状态设置为true;

(2) 如果 t 线程处于阻塞状态时,此时interrupt()会通过触发异常,将阻塞状态(sleep)提前唤醒,但是由于把sleep唤醒会导致标志位变成false,也就是将标志位清空,如果标志位本身就是false则不变.由于我们的循环条件是while(!Thread.currentThread().isInterrupted()),又因为我们的标志位又变成了false,所以我们可以继续执行这个循环.

实际中能产生阻塞的方法有很多,并不是只有sleep,这里只是用sleep举例子.

由于计算机的执行速度时很快的,所以阻塞状态大概占线程执行状态的99.99%,所以会有更大的几率触发(2).

当然,即使是大部分时间都在阻塞状态,但是该线程始终是在阻塞状态和正常运行的状态之间切换.

当然,我们可以通过break关键字,当抛出异常时直接break就可以不再执行循环,之后就可以结束线程,这就相当于我正在打游戏,我妈喊我吃饭,此时我不敢抗拒,只能去吃饭.

代码如下:

public class ThreadDemo5 {public static void main(String[] args)  {Thread t = new Thread(()->{while (!Thread.currentThread().isInterrupted()){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();break;}System.out.println("线程运行中~~");}System.out.println("t 线程结束");});t.start();try {//睡眠3000msThread.sleep(3000);//将标志位设置为false终止上面的while()System.out.println("控制新线程结束");t.interrupt();} catch (InterruptedException e) {e.printStackTrace();}}
}

此时的运行结果:

在这里插入图片描述
当然,由于我们捕捉到了异常,我们不仅可以直接让他break,还可以让他先等待一会再结束执行.

就像我们在打电话一样:

比如我正在打游戏,女朋友让我出去买菜,此时我不搭理她,这个时候就相当于,你说你的我干我的.

但是呢?过了一会女朋友又来跟我说让我买菜去,我因为怕她生气嘛,所以我跟她说,五分钟,五分钟就好…等五分钟一到,我就退出游戏买菜去了.

当我买完菜的时候,刚躺到床上打开游戏,此时厨房传来女朋友的尖叫声,那我必须立刻放下手机去看看发生了什么事情…

总结三种状态;(1)立即退出 (2) 等待一会再退出 (3) 不退出,你说你的我做我的.

由我们自己来决定什么时候退出.

如果是直接写死的话,让我收到命令就必须退出,假如我在跟老板汇报工作,此时女朋友让我去买菜,如果我把老板电话挂掉,那自己也就寄了.

只有自己才有资格决定自己要做什么事情,别人的话之能当作意见,只有自己是为自己好.

等待一个线程

线程是一个随机调度过程,等待线程,就是控制两个线程的结束顺序.

但是呢?我们可以通过过join()方法来控制线程的执行顺序.

大家看下面的代码以及执行结果:

在这里插入图片描述
由于线程是抢占式执行的,所以可能这次是先打印 main 但是下次可能就先打印t了,可是程序员不喜欢这种不确定性,所以我们可以通过join()方法来控制顺序.

大家看下面的代码和执行结果:

在这里插入图片描述
当我们在 main 线程中调用 t.join() ,此时只有当 t 线程执行完之后,才会去执行 main 线程中的内容,我们也可以理解为,在哪个线程中调用join()方法,那么就哪个线程阻塞,只有当调用这个方法的引用所对应的线程执行完之后,才可以继续执行后续代码~

在这里插入图片描述
当然,这个join()也是可以传参数的,假如 t 线程一直没有执行完,那么我这个线程就不工作了? 就像我约女神见面一样,我等了一天,两天,三天都没等到,难道我还要等一辈子? 所以此时我们就可以传一个参数,作为最大等待时间,超过这个时间我就不等了,世界上美女那么多,我换个人舔还不行?

join方法自身也会阻塞,Java线程但凡是阻塞的方法都可能抛出这个异常(throws InterruptedException)。

join有两种行为:

(1)如果被等待的线程还没有执行结束,那么直接阻塞等待;

例如:我接我孩子放学,他5.00放学,我4.50到了,此时我就需要等待他放学,菜才能回家.

(2)如果被等待的线程已经执行结束了,就直接返回。

例如:我接我孩子放学,他5.00放学,我5.10才到,那么我就不用等,可以直接接回家.

join方法不带参数的时候,就是死等,一直等待下去。方法带参数的时候,如果超过了等待的最大时间,就开始执行下一步的代码。一般写带参数的join方法。

相关文章:

java线程之Thread类的基本用法

Thread类的基本用法1. Thread类的构造方法2. Thread的几个常见属性常见属性线程中断等待一个线程小鱼在上一篇博客详细的讲解了如何创建线程,java使用Thread类来创建多线程,但是对于好多没有相关经验的人来说,比较不容易理解的地方在于操作系统调度的执行过程. 我们通过下面代码…...

【js】多分支语句练习(2)

个人名片: 😊作者简介:一名大一在校生,web前端开发专业 🤡 个人主页:python学不会123 🐼座右铭:懒惰受到的惩罚不仅仅是自己的失败,还有别人的成功。 🎅**学习…...

Redis与MySQL的双写一致性问题

Redis与MySQL的双写一致性问题更新缓存? 删除缓存?先更新缓存再更新数据库先更新数据库,再更新缓存先删除缓存再更新数据库先更新数据库,再删除缓存解决方案1. 重试2. 异步重试2.1 使用消息队列实现重试2.2 Binlog实现异步重试删除…...

Java基础:笔试题

文章目录Java 基础题目1. 如下代码输出什么?2. 当输入为2的时候返回值是多少?3. 如下代码输出值为多少?4. 给出一个排序好的数组:{1,2,2,3,4,5,6,7,8,9} 和一个数,求数组中连续元素的和等于所给数的子数组解析第一题第二题第三题第四题方案…...

spring三级缓存以及@Async产生循环引用

spring三级缓存以及Async产生循环引用spring三级缓存介绍三级缓存解除循环引用原理源码对应1、获取A,从三级缓存中获取,没有获取到2、构造A,将A置入三级缓存构造A(创建A实例)置入缓存3、注入属性,构造B扫描缓存实例的相关信息注入…...

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(5)

目录 写在前面: 题目:P2036 [COCI2008-2009#2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述: 输入格式: 输出格式: 输入样例: 输出样例: 解题思路: 代码…...

【Unity3D】Unity3D中在创建完项目后自动创建文件夹列表

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 随着项目开发的体量增大,要导入大量的素材、UI、模…...

如何设计一个锂电池充电电路(TP4056)

这个是个单节18650锂电池的充电模块,这个是个18650的锂电池,18指的是它的直径是18mm,65指的是它的高度为65mm。这个18650电池的标称电压是3.7V,电池充满时电压为4.2V,一般电池电压越高也就代表它所剩的电量越大。这种锂…...

Spark了解

目录 1 概述 2 发展 3 Spark和Hadoop 4 Spark核心模块 1 概述 Apache Spark是一个快速、通用、可扩展的分布式计算系统,最初由加州大学伯克利分校的AMPLab开发。 Spark可以处理大规模数据处理任务,包括批处理、迭代式算法、交互式查询和流处理等。Spa…...

c++STL急急急

文章目录cSTL急急急vector头文件扩容过程用法:size/emptyclear迭代器begin/endfront/backpush_back() 和 pop_back()queue头文件用法循环队列 queue用法优先队列 priority_queue用法stack头文件deque头文件deque中控器:用法set头文件用法迭代器begin/end…...

【C++学习】模板进阶——非类型模板参数 | 模板的特化 | 分离编译

🐱作者:一只大喵咪1201 🐱专栏:《C学习》 🔥格言:你只管努力,剩下的交给时间! 模板我们之前一直都在使用,尤其是在模拟STL容器的时候,可以说,模板…...

【C++】C++11新特性——可变参数模板|function|bind

文章目录一、可变参数模板1.1 可变参数的函数模板1.2 递归函数方式展开参数包1.3 逗号表达式展开参数包1.4 empalce相关接口函数二、包装器function2.1 function用法2.2 例题:逆波兰表达式求值2.3 验证三、绑定函数bind3.1 调整参数顺序3.2 固定绑定参数一、可变参数…...

ssm框架之spring:浅聊事务--JdbcTemplate

简介 JdbcTemplate 是 Spring 对 JDBC 的封装,目的是使JDBC更加易于使用,JdbcTemplate是Spring的一部分。JdbcTemplate 处理了资源的建立和释放,它帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流&…...

盘点Python那些简单实用的第三方库

文章目录前言关于本文使用 pip 命令下载第三方库1、phone 库(获取手机号码信息)2、geoip2 库(IP 检测功能)3、freegames 库(免费小游戏)4、jionlp 库(解析地址信息)5、pyqrcode 库&a…...

leetCode热题21-26 解题代码,调试代码和思路

前言 本文属于特定的六道题目题解和调试代码。 1 ✔ [160]相交链表 Easy 2023-03-17 171 2 ✔ [54]螺旋矩阵 Medium 2023-03-17 169 3 ✔ [23]合并K个排序链表 Hard 2022-12-08 158 4 ✔ [92]反转链表 II Medium 2023-03-01 155 5 ✔ [415]字符串相加 Easy 2023-03-14 150 6 …...

ChatGPT推出第四代GPT-4!不仅能聊天,还可以图片创作!

3月15日凌晨,OpenAI震撼发布了多模态预训练大模型 GPT-4。 根据官网发布的通告可以知道,GPT-4 实现了以下几个方面的飞跃式提升:强大的AI创作识图能力;文字输入限制提升至 2.5 万字;回答准确性显著提高;能够…...

二叉搜索树:AVL平衡

文章目录一、 二叉搜索树1.1 概念1.2 操作1.3 代码实现二、二叉搜索树的应用K模型和KV模型三、二叉搜索树的性能分析四、AVL树4.1 AVL树的概念4.2 AVL树的实现原理4.3 旋转4.4 AVL树最终代码一、 二叉搜索树 1.1 概念 二叉搜索树( Binary Search Tree,…...

数据结构和算法(1):数组

目录概述动态数组二维数组局部性原理越界检查概述 定义 在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识 In computer science, an array is a data structure consisting of a col…...

python+django+vue全家桶鲜花商城售卖系统

重点: (1) 网上花店网站中各模块功能之间的的串联。 (2) 网上花店网站前台与后台的连接与同步。 (3) 鲜花信息管理模块中鲜花的发布、更新与删除。 (4) 订单…...

一文带你领略 WPA3-SAE 的 “安全感”

引入 WPA3-SAE也是针对四次握手的协议。 四次握手是 AP (authenticator) 和 (supplicant)进行四次信息交互,生成一个用于加密无线数据的秘钥。 这个过程发生在 WIFI 连接 的 过程。 为了更好的阐述 WPA3-SAE 的作用 …...

电商网站SEO网站结构应该如何设计

电商网站SEO网站结构设计的关键点 在当今数字化时代,电商网站的成功离不开搜索引擎优化(SEO)。一个精心设计的网站结构不仅能提升网站的用户体验,还能大大提高在搜索引擎上的排名。电商网站SEO网站结构应该如何设计呢&#xff1f…...

【仅限首批认证用户开放】Polars 2.0企业清洗最佳实践白皮书(含GDPR脱敏DSL语法速查表)

第一章:Polars 2.0企业级数据清洗能力全景概览Polars 2.0 将数据清洗从“脚本式修补”推向“工程化流水线”,依托零拷贝内存模型、并行执行引擎与声明式 API,原生支持高吞吐、低延迟、强一致性的清洗任务。其核心能力不再依赖 Pandas 风格的链…...

爬虫自动化(DrissionPage)

目录 ?一.介绍: 下载DrissionPage,还是我们熟悉的pip: 环境准备: ?二.基本代码: 它对于的导包和类使用: 窗口的设置: 和获取的页面的滑动: 3.进一步认识DrissionPage: 浏览器可以多开…...

COMSOL激光打孔形貌优化:不同入射角设置方法与模型注释解析

COMSOL 不同激光入射角打孔形貌设置方法 模型内容:不同激光入射角度的设置 优势:视频教学和模型注释清晰明了,各个情况都有涉及可参考性极强,可以修改,收敛性已调至最优,本案例可进行拓展应用服务&#xff…...

多个source、多个sink

关键配置:sink的:plugin_input ["source_data1", "source_data2"]对应模型┌──────────┐│ Source A │──┐└──────────┘ │├──▶ Sink┌──────────┐ ││ Source B │──┘└──────…...

别再混淆了!一文讲透NvDecoder里ulNumDecodeSurfaces和ulNumOutputSurfaces到底怎么用

深入解析NvDecoder:解码缓存与输出缓存的本质区别与实战配置 在视频处理领域,NVIDIA的硬件解码器(NVDEC)因其出色的性能和高效的资源利用率而广受开发者青睐。然而,对于许多中高级开发者来说,NvDecoder中ul…...

如何高效优化Windows系统性能:AtlasOS完整调优指南

如何高效优化Windows系统性能:AtlasOS完整调优指南 【免费下载链接】Atlas 🚀 An open and lightweight modification to Windows, designed to optimize performance, privacy and usability. 项目地址: https://gitcode.com/GitHub_Trending/atlas1/…...

STC89C52内存告急?手把手教你优化MPU6050 DMP库,让51单片机也能流畅跑姿态解算

STC89C52内存告急?手把手教你优化MPU6050 DMP库,让51单片机也能流畅跑姿态解算 当你在STC89C52这类资源有限的51单片机上尝试运行MPU6050的DMP(Digital Motion Processor)库时,是否遇到过编译失败或运行不稳定的情况&…...

升级版会议纪要录音转文字工具 识别准转得快 整理省事体验好

前前后后踩过不下10款录音转写工具的坑,要么错字多到要逐行改,要么转出来的内容逻辑混乱,得花好几个小时捋顺,直到用到2026升级版的会议纪要录音转文字工具,才真的感受到什么叫识别准、转得快、整理省事体验好。今早开…...

Jmeter接口测试项目实战

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 1、什么是jmeter?JMeter是100%完全由Java语言编写的,免费的开源软件,是非常优秀的性能测试和接口测试工具,支持主流…...