当前位置:网站首页>Thread类的基本使用。
Thread类的基本使用。
2022-08-04 08:01:00 【LIn_jt】
Thread类的基本使用。
Thread类用于操作线程,涉及到对线程操作的各个方法。本文将介绍Thread类的各个细节及其Thread类的使用
1.线程的创建
创建线程有很多种方式,首先我们来看第一种 继承Thread类
继承Thread类创建线程
在这里我们新建了一个类thread1,并且通过它来继承我们饿Thread类,要注意的是,Thread类是实现了Runable接口的,在Runable接口中有一个抽象方法run(),我们通过重写它来实现自己的代码逻辑
类建完了之后,我们接着来真正创建一个线程:>
这两条语句,就能真正帮我们创建出一个线程,要注意的是,这里必须通过start()方法开启一个线程,而不能直接去调用我们的run方法,run方法只是一个普通的方法而已。我们如果进入start()方法中:>
就会发现,start中又调用了start0()方法,start0()方法再真正地开启一个线程。
start0是一个本地方法,我们是无法查看它的实现的。
实现Runnable接口创建线程
Thread类是实现了Runnable接口的,同理,我们也可以创建一个类来实现Runnable接口,请看以下代码:>
这里我们thread02类实现了Runnable接口,那么我们就必须得重写里面的抽象方法run。同样的,我们再以thread02去获取一个线程对象。
Thread类中是有一个构造方法可以传入Runnable类型的对象的!
同理,再通过thread.start()开启一个线程。
通过Thread的匿名内部类
这里的new Thread(){代码},本质上就是获得一个Thread的子类对象,我们也可以通过这样的方式创建一个线程。再通过start方法就可以开启线程!
通过Runnable的匿名内部类
与上面同理,请看下面代码
这里我们分了两步,先获得了一个Runnable接口,再将这个Runnable接口传给Thread。但实际上,我们可以直接一步走:>
直接在Thread的构造方法中传入一个实现了Runable接口的对象。
通过lambda表达式创建线程
通过lambda表达式也能创建一个线程,这也是笔者最近比较喜欢创建线程的方式。
2.线程中断
接下来是我们的线程中断。首先理解一下线程中断的概念。
线程中断:即正在运行中的线程被强制打断。我们有以下几种方式来对线程进行中断,请看下文:>
1.使用自己的标志位
在以上代码中,我们自己创建了一个标志变量flag,我们希望通过这个变量,去控制t1线程退出。最后对flag的设置,即让线程t1终止
while循环,这样的话就能结束我们的t1线程了,画一个流程图之后,是这样子的:>
上述代码最需要理解的地方是,在执行红圈部分的前两条部分时,此时t1线程仍然在执行,也即仍然在打印(线程执行中),这是因为线程是独立的执行流。当main主线程结束时,在这里t1也不会立即结束,而是会执行完自己的代码逻辑后,才结束。
2.使用Thread自带的标志位。
currentThread
在使用Thread自带的标志位之前,首先我们需要熟悉两个方法,一个是Thread里的currentThread。一个是isInterrupted方法。
currentThread方法,主要是用于获取当前线程的实例,谁去调用这个方法,就获得该线程的实例,比如t1线程去调用currentThread方法,就返回t1线程的thread对象 。帮助文档中,是这么说明这个方法的
isInterrupted方法
isInterrupted方法是用来判断此时线程的内置位为true还是false,等下我们将以代码的形式来对它进行进一步的理解。我们来看一下帮助文档对该方法的说明
在了解了以下这两个方法之后,我们对我们上述给出的通过改自己的标志位进行控制线程的代码进行改进,请看下图
划红圈的地方是我们修改的地方,此处要注意的是
这就是我们上面所说到的两个函数的利用了,我们先不看下面的红框,我们先来看一下运行结果:>
我们发现,程序在打印出错误信息之后,线程仍然在跑,为什么?,这就不得来说说interrupt方法。
interrupt有两种行为,在此处我们以线程t1为例。
1.如果线程不处于阻塞状态下,那么interrupt会将内置的标志位设为true;
2.如果线程处于阻塞状态下,那么interrupt就会让线程阻塞的方法,在这里是sleep, 抛出异常。
写到这里可能你会提问,既然在interrupt让线程,为什么这个线程还会仍然执行呢,这就涉及到了我们的处理方面,我们来看一下上图中的try-catch
可以看到,在这里sleep抛出异常过后,只是被异常对象捕获,并不会直接终止循环,这里catch之后怎么处理,是交给我们自身来做的,你可以设置立即退出,也可以设置过一段时间之后再退出,也可以不退出。
立即退出的情况:>
在catch中我们写一条break语句,这样catch在捕捉到异常之后,遇到break语句就直接跳出循环了。执行结果为:>
线程稍后退出:>
这里我们用了sleep方法来演示线程的收尾工作,这里的代码你可以更换成你自己想要的代码逻辑。
线程不退出:>
字面理解就是我们直接忽视掉异常,让线程继续执行,这里的代码是这样的
在catch中我们啥也不干,这样的话线程即使抛出异常也会继续执行。
3.线程等待
首先请看以下这段代码
这里我们是要来计算两个线程同时运行所执行的时间,并且我们在main方法中调用这个方法。
先不说那么多,这段代码正确吗? 可以很直白的说,不正确。首先请看:>
前面两个红框的部分,main开启了两个子线程t1和t2:
但要注意的是,此时语句会继续向下走,也即会继续执行这两句代码
这就与我们所想象的逻辑不相同,我们本意是想让两个线程执行完成之后,再完成计时。而现在的情况是,线程还没有执行完全,就已经记好时了。既然问题在这里,我们想到,能不能让线程先执行完,再计时呢?其实是可以的,这就需要我们的join方法登场了。
join方法用大白话来说的话,就是线程插队。比如说以下这个例子
现在线程t1执行执行着,调用了一个t2.join方法
这个时候,线程t1就会进入阻塞状态,线程t2开始执行,并且等到t2执行完之后,再会转而去执行t1
这样的话,我们就可以对我们的代码进行修改了,请看:>
这样的话,main函数在执行t1.join()时,main线程会阻塞等待t1执行完,在执行t2.join时,main线程也会阻塞等待t2执行完。这就达到了我们的效果。
说到join,我们不得不说一下它的好兄弟,ylied方法。
yield()方法,用大白话来说,就是线程礼让,它能够让正在执行的线程进入就绪状态等待调度,也就是说,我把cpu让给你,但你抢不抢得到,看你自己本事~~~。
4.线程休眠
首先来看一下线程休眠的概念:>
线程休眠:在当前线程的执行中,暂停指定的毫秒数,释放CPU的时间片.
在本文的上面代码中,我们用的Thread.sleep(1000) (这里的1000是我们传进去的参数,单位是ms);就是一种让线程休眠的方法。
需要注意的是,在哪个线程调用sleep方法,哪个线程就将进入休眠状态。
在这里我们是在t1线程里面调用的sleep方法,因此在执行该条语句之后,t1线程将进入休眠状态。
此外,我们还需了解sleep的底层原理。
在上图中,我们将就绪队列和阻塞队列抽象出来,如果此时一个就绪队列调用了sleep方法,那么该线程就将从就绪队列到阻塞队列中去,即:>
此时这个线程会等待sleep时间结束再重新回到就绪队列,等待调度。
5.获取线程对象
获取线程对象有两种方法,其中一种我们已经在上述介绍Thread.currentThread()中讲到。还要注意的是,还有另外一种方法。
如果是在一个继承了Thread类的类中,并且重写了run方法,那么我们可以在run中用this来获取当前线程对象。
但感觉不如一个Thread.currentThread()来的简单实在。
边栏推荐
猜你喜欢
解决:Hbuilder工具点击发行打包,一直报尚未完成社区身份验证,请点击链接xxxxx,项目xxx发布H5失败的错误。
Redis非关系型数据库
智能健身动作识别:PP-TinyPose打造AI虚拟健身教练!
【论文笔记】—低照度图像增强—Supervised—RetinexNet—2018-BMVC
babylon 里面加gltf 模型
【虚幻引擎UE】UE5基于Gltf加载插件实现gltf格式骨骼动画在线/本地导入和切换
高等代数_证明_对称矩阵属于不同特征值的特征向量正交
[想要访问若依后台]若依框架报错401请求访问:error认证失败,无法访问系统资源
经典二分法查找的进阶题目——LeetCode33 搜索旋转排序数组
YOLOv5应用轻量级通用上采样算子CARAFE
随机推荐
IDEA引入类报错:“The file size (2.59 MB) exceeds the configured limit (2.56MB)
安装GBase 8c数据库集群时,报错误码:80000306,显示Dcs cluster not healthy。怎么处理错误呢?
QT + msvc2017编译器
经典递归回溯问题之——解数独(LeetCode 37)
实现加载驱动、得到数据库对象、关闭资源的代码复用,将代码提取到相应的工具包里边。优化程序
开发小技巧 navicate如何点击单元格显示全部的文本内容或通过图像查看内容
<jsp:useBean>动作的使用
智能健身动作识别:PP-TinyPose打造AI虚拟健身教练!
解决:Hbuilder工具点击发行打包,一直报尚未完成社区身份验证,请点击链接xxxxx,项目xxx发布H5失败的错误。
redis stream 实现消息队列
金仓数据库 KDTS 迁移工具使用指南 (6. 注意事项)
form表单提交到数据库储存
经典二分法查找的进阶题目——LeetCode33 搜索旋转排序数组
1161. Maximum Level Sum of a Binary Tree
新特性解读 | MySQL 8.0 在线调整 REDO
Typora_Markdown_图片标题(题注)
MotionLayout的使用
C语言strchr()函数以及strstr()函数的实现
金仓数据库 KDTS 迁移工具使用指南 (5. SHELL版使用说明)
【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解