多线程(2)

news2025/1/14 18:33:20

文章目录

  • 前言 :
  • 1.Thread类 :
    • 1.1 Thread类常见的构造方法
    • 1.2 Thread的几个常见属性
    • 1.3 中断一个线程
    • 1.4 等待一个线程-join()
    • 1.5 获取当前线程引用
    • 1.6 休眠当前线程
  • 2.线程状态

前言 :

简单回顾上文知识点

上文我们了解了 线程是为解决并发编程引入的机制.

知道了线程相比于进程来说更轻量 ( 创建线程比创建进程开销更小,销毁进程比销毁进程 开销更小, 调度线程比调度进程开销更小) 。

另外我们的进程是包含线程的 , 所以同一个进程中的诺干个线程之间共享同一份内存资源和文件描述符表,

虽然进程之间是共享同一份资源 , 但是线程之间都是可以独立调度执行的,且每个线程都有自己的状态 / 优先级 / 上下文 / 记账信息 .

除了以上这几点 ,还总结果两点 :

1.进程是操作系统分配的基本单位

2.线程是操作系统调度执行的基本单位

通过 Thread 类 创建线程的几种方法

1.继承Thread 重写 run

2.实现 Runnable 重写 run

3.使用匿名内部类, 继承 Thread

4.使用匿名内部类,实现 Runnable

5.使用lambda 表达式

注意 : 这里是离不开 Thread的, 上面的几种方式 只是使用了不同的方式来描述 Thread里的任务是啥 。

同时这里的几种方法创建出来的线程是一样的 .

最后回顾一下 Thread 的 run 和 start 的区别

run : 只是一个普通的方法, 描述线程执行的任务是上面

start : 当我们调用 start 方法时才会去创建线程, 并执行run里面的方法.

下面就来开始本文的学习 :

上文 我们已经通过 Thread类创建了我们的线程, 下面就来了解一下 Thread类 和 方法

1.Thread类 :


概念 :

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。

Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理

1.1 Thread类常见的构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可


这里 Thread(String name) 和 Thread(Runnable target , String name) 与之前相比只是多了一个参数 name , 主要的作用就是为我们的线程起名,方便我们后面进行调试 , 这里线程的默认名字 为 thread-0 之类的…

如 thread - 1, 2 , 3 等, 线程一多就很难分辨,所以命名操作比较重要。


下面就来演示一下 :


图一 : 创建线程, 并将线程命名为 mythread , 并启动
在这里插入图片描述

下面就可以通过上文所讲的 jconsole 来查看我们的线程, 这里就不赘述如何找到jdk的jconsole

图二 :

在这里插入图片描述


注意 :

在这里插入图片描述


常见的构造方法看完来看看我们的Thread的常见属性

1.2 Thread的几个常见属性

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


getName() : 这里获取到的就是我们再构造方法里面取的名字


getState() : 线程状态


注 :java里面的线程状态要比操作系统原生的状态更丰富一点


getPriority() : 这个可以获取线程的优先级 ,也可以设置,但设置了也没有啥用


isDaemon() : 这里是判断是否为守护线程,这里翻译成守护线程可能会让人尝试误会,所以这里更推荐说成是否为后台线程 :

那么啥是后台线程呢 ?

简单来说 : 到了 中午 12点 ,吃饭,但我们手头上有一件事情,比较重要 紧急,必须做完才能去吃 , 这里就是前台线程,必须等线程任务执行完才能结束线程.

后台线程 : 同样是 12 点 吃饭,手头上同样有一件事情,但是不那么 重要,紧急 ,此时就可以直接放下手中的事情,去吃饭. 就是说后台线程 ,不会因为线程的工作 而导致线程不结束.


前台线程 :会阻止进程结束,前台线程的工作没有做完,进程是完成不了的
在这里插入图片描述


后台线程 : 不会阻止进程结束 , 后台线程工作没做完,进程也可以结束的.

注意 : 代码里面手动创建的线程,默认都是前台线程, 包括 main默认也是前台线程。

其他 jvm 自带的线程都是后台的, 这里我们也可以使用 setDaemon 设置成后台线程.


演示 通过 setDaemon 将 t 线程改为 我们的后台线程

在这里插入图片描述


isAlive() : 是否存活


这里判断的系统里面的线程是否存活


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


另外 : 多个线程再微观上可能是并行的(多个核心), 也可能是并发的(一个核心) ,宏观上感知不到(应用程序) , 所以我们看到的就始终是随机执行,抢占式调度的.


属性看完下面来看一下我们如何中断一个线程

1.3 中断一个线程


注意 : 这里中断的意思 ,不是让线程立刻就停止,而是通知线程 ,你应改要停止了,是否真的停止,取决于线程这里具体的写法 .


举个简单的例子 :

这里我们处于打游戏 的时候, 突然 母亲叫我们去超市带一瓶酱油 此时我们有三种做法

1.放下游戏,立刻就去

2.打完这把,再去,稍后处理

3.假装没听见,就完全不处理


此时 这里是直接去还是拖一会,还是直接装傻充愣 不去都是我们自己的选着 。


在我们线程中 就有两种比较典型的做法


1.通过共享的标记来进行沟通

在这里插入图片描述


2.调用 interrupt() 方法来通知


上面自定义变量的方法 会有一个很大的缺点, 它不能及时响应, 尤其是在 sleep 休眠的时间比较久就的时候 .

在这里插入图片描述


这里我们就可以使用 Thread 自带的标志位,来进行判断


图一 :
在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


注意 : 这里是 sleep 清空我们的标志位 .


之前我们说过 线程的执行的随机调度的,所以我们并不能够知道谁前谁后,正因为它的不可预期性,这里我们普变更喜欢能够预期的,知道谁先执行谁后执行 , 这里我们就可以通过 等待线程,来控制两个线程的结束顺序.

1.4 等待一个线程-join()


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


好比 :放学接娃, 原本 5点放学, 我们 4.55 到,此时我们就需要等待孩子放学,到了时间我们就可以将孩子接走,如果 我们 5.05到,那就不是我们等孩子了而是孩子等我们, 当我们到了就直接将孩子带走即可.


另外 我们直接使用 join() 会有一个问题,就是死等的问题, 啥是死等呢 ?

就相当于当于一条舔狗,一直舔一个女神,不管多久就舔她一个,这里就可以看成死等 , 没有舔到 , 就一直就 舔。

这里就非常不好,即便是舔狗也是有原则的,也是有底线的,舔不到就不舔了,换一个。


join 为了解决死等的问题 , 就提供了另外一个版本 ,即 join(long millis) 此时就可以指定等待的时间, 这里的参数就是线程能够等待的最长时间 , 当时间到达了 这个等待的时间,就不会在等待了 直接执行下面的操作。

1.5 获取当前线程引用

方法说明
public static Thread currentThread();返回当前线程对象的引用


这个方法在 中断一个线程就出现了,到这里因该是比较熟悉的 ,所以这里就直接一笔带过 。

在这里插入图片描述


题外话:

public static Thread currentThread() ;

这里我们一般都称 被 static 修饰的方法 是静态方法但是静态方法这个名字不太好 , 更推荐 称为 类方法 。


类方法 : 调用这个方法,不需要实例,直接通过类名来调用。

如果 : Thread t = new Thread() Thread 是 类 , t 是实例 , 那么 对于 currentThread() 方法, 就可以直接 Thread.currentThread() 调用 (返回值正是这个引用指向的对象), 而不一定非得

t.currentThread() , 这里 直接使用类名调用 就是类方法 。

另外 : 通过 t.currentThread() 也是可以调用的, 但这么做,本质上 还是通过实例找到类, 再通过类名的方法调用. (javase 语法中也提过这个,这样写编译器会报警告).

1.6 休眠当前线程

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠


线程休眠 : 本质上就是让这个线程不参与调度了(不去CPU上执行) 。

好比之前进程中的 海王例子 : 不是说海王谈了3个女朋友 A B C , 假如 B 去出差了 , 此时海王就没有机会去和 B约会了, 那么 B 就相当于暂时不参与调度了.

此时 只有 A 和 C 能进行调度 ,当 B回来之后 ,才能重新参与调度 .

在这里插入图片描述

2.线程状态


之前在 进程的那篇文章,说过状态 是进程的一种属性但是这种说法并不是太严谨, 为啥之前说呢?

还是哪一个点, 之前的文章是针对进程中只有一个线程的情况,所以才去这么说的, 但大多数情况下一个进程对应着一组PCB(每个PCB对应这一个线程) , 此时操作系统调度线程, 就可以通过线程的状态来判断是否需要调度 就绪就直接调用 ,阻塞就换一个处于就绪的线程 . 所以以后听到状态更应该是线程的属性了, 那么后面谈到状态都应该先考虑线程。

在这里插入图片描述


我们 主要讨论 两种状态 1.就绪, 2. 阻塞 , 但是 java 对于线程的状态,进行了细化操作 , 也就分出来了 6中状态, 下面就来看看.

1.NEW :创建了 Thread对象, 但是还没调用 start(操作系统内核还没创建对应的PCB)

2.TERMINATED : 表示内核中的PCB 已经执行完毕了, 但Thread对象还在.

3.RUNNABLE : 可以运行的 (注意 : 这里叫 RUNNABLE ,而不是 RUNNING 正在运行

这里有两种情况

a : 正在CPU上执行的

b : 在就绪队列里,随时可以去CPU上执行 .


4.WAITNG

5.TIMED_WAITING

6.BLOCKED


这里 4 , 5 , 6 都是阻塞 , 都是表示 线程 PCB正在阻塞队列中 , 这几种状态 都是不同原因的阻塞.

在这里插入图片描述


简单画一个 线程的转化 :

在这里插入图片描述


另外 这里也有 复杂的 :个人感觉简单的图足够 了 .

在这里插入图片描述


下面就来通过 代码 来触发一下这几个状态 :


1.NEW :创建了 Thread对象, 但是还没调用 start(操作系统内核还没创建对应的PCB)

在这里插入图片描述


2.TERMINATED : 表示内核中的PCB 已经执行完毕了, 但Thread对象还在.

在这里插入图片描述

补充 :

TERMINATED 这个状态其实并没有啥用 , 因为一旦内核里的线程 PCB 消亡了, 此时 代码中 t 对象 也就没啥用了。

为啥存在呢 ?

其实迫不得已 , java中对象的生命周期,自有其规则,这个生命周期和系统内核里面的线程并非完全一致 , 内核的线程释放的时候,无法保证 Java 代码中 t 对象也立即释放 .

因此 , 势必就会存在 ,内核的 PCB 没了,但是代码中的 t 还存在的情况 , 此时就需要通过特定的状态,来将 t 对象表示成 “无效的状态” 。

另外这里 不能 重新 start , 也就是不能重新创建线程 , 这里一个线程 只能start 一次 .


3.RUNNABLE : 可以运行的 (注意 : 这里叫 RUNNABLE ,而不是 RUNNING 正在运行


这里有两种情况

a : 正在CPU上执行的

b : 在就绪队列里,随时可以去CPU上执行 .

在这里插入图片描述


如果再 run 里面写了 sleep 之类 的方法 让线程阻塞了 , 就不是 RUNNABLE


4.TIMED_WAITING

在这里插入图片描述


5.WAITNG

6.BLOCKED

最后的这两个 后面说 .

到此 Thread 类常见的方法 和 线程的状态就差不多说完了, 下面我们来通过代码来看看 :

单个线程和多个线程之间的执行速度的差别 (CPU 密集的程序中差别是非常明显的) .


小补充 :

其实 程序主要分为 :

CPU 密集 : 包含了大量的加减乘除等算术运算

IO 密集 : 涉及到读写文件, 读写控制台,读写网络


这里就可以假设一个场景 : 有一个运算量非常大的任务 , 这里就分别通过进程 (单个线程) 和 多线程分别执行,观察他们开始执行时间 和 结束程序时间之差。


1.使用单个线程

在这里插入图片描述

2.使用两个线程

在这里插入图片描述


对比一下 : 使用单个线程 执行的时间为 484ms , 使用两个线程执行的时间是 270ms , 可以看到时间缩短的很明显。

但是就有一个问题 :为啥不是正好缩短一半?

答案 : 因为多线程可以更充分的利用到多核心 cpu 的资源,因此使用多线程的时间会快 ,但是 我们并不能知道 t1 和 t2 是分布再两个 CPU上执行还在一格cpu上 先执行 t1 然后 执行 t2 ,绕一圈回来 又执行t1. (这里就是并行和并发)

另外一点我们的线程调度 自身也是又时间消耗的.

所以这的时间就很难缩短到一半 。


所以 : 多线程 在这种CPU密集型的任务中,有非常大的作用,可以充分的利用 CPU 的多核资源,从而加快程序的运行效率


另外 : 并不是使用多线程 就能一定提高效率

还需要考虑 1. 是否是多核 , 2. 当前核心是否空闲(如果CPU 这些核心都满载了,这个时候启动跟多的线程也就没啥用)。

上面说 多线程 在 密集型程序有明显的作用, 其实多线程在 IO密集型的任务中,也是有作用的 .

举例 : 在日常使用的一些程序中,经常会看到 “程序未响应” (如 :启动 某款程序 ) 为啥呢 ?

因为 程序 进行了 一些耗时 的 IO 操作 (比如打开 dota2 启动 加载数据文件 ,就涉及到了 大量的读硬盘操作 ) , 阻塞了界面响应 ,这种情况下使用多线程也就可以有效改善(一个线程赋值IO , 另外一个线程用来响应用户的操作).

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux网络编程——IO多路复用

文章目录1,I/O模型2,阻塞I/O 模式2.1,读阻塞(以read函数为例)2.2,写阻塞3,非阻塞I/O模式3.1,非阻塞I/O模式的实现(fcntl()函数、ioctl() 函数)3.1.1&#xff…

Apollo 应用与源码分析:Monitor监控-软件监控-channel时间延迟监控

目录 代码 分析 主要结构 判断逻辑 备注 代码 class ChannelMonitor : public RecurrentRunner {public:explicit ChannelMonitor(const std::shared_ptr<LatencyMonitor>& latency_monitor);void RunOnce(const double current_time) override;private:static …

有限元求解:结构应力法如何实现的网格不敏感呢?

作者&#xff1a;云兵老师&#xff0c;仿真秀专栏作者 一、导读 本人从大一时&#xff0c;上第一节力学课就开始接触有限元&#xff0c;那个时候老师曾说&#xff1a;“学会我讲的这门有限元&#xff0c;我可以保证你在毕业时候一定找到工作”。那个时候有限元很火&#xff0…

基于ANSYS Polyflow的逆向挤出模头设计攻略

摘要&#xff1a;内侧灯罩属于复杂截面塑料异型材&#xff0c;目前其挤出模头的设计主要依赖于经验&#xff0c;需要反复试模和修模&#xff0c;使得模具质量难以保证&#xff0c;生产周期长&#xff0c;成本高。 本文采用数值模拟方法对内侧灯罩进行了反向挤出模头设计。首先…

若依框架前后端打包到linux部署,踩坑

前后端分离版下载地址 https://gitee.com/y_project/RuoYi-Vue 打开前端项目 打开readme 初始化依赖 npm install --registryhttps://registry.npmmirror.com启动项目 npm run dev路由模式修改为hash 否则打包后请求路径会和后端冲突 src/router/index.jsexport default ne…

省 市 县 三级联动

大纲 一、导入省市县数据表(t_region) 二、引入jar包 三、导入所需util类&#xff08;整体框架&#xff09; 四、编写代码 1、配置数据库相关信息(数据库名、用户名、密码) config.propreties #oracle9i #driveroracle.jdbc.driver.OracleDriver #urljdbc:oracle:thin:loca…

Linux下redis安装教程

redis安装教程 首先需要安装gcc依赖 yum install -y gcc tcl注&#xff1a;这里如果安装失败可以百度解决 进入/usr/local/src目录&#xff0c;将下载的安装包放在该目录 cd /usr/local/src解压该目录 tar -zxvf redis-6.2.6.tar.gz解压后src下面多了一个redis-6.2.6 进入…

Redis 为什么这么快,你知道 I/O 多路复用吗?

今天我们讨论一下面试高频题&#xff0c;为什么 Redis 那么快&#xff1f; 首先&#xff0c;你可以先想一下答案&#xff0c;我先说下大家普遍的答案&#xff1a; 单线程基于内存操作&#xff0c;速度快I/O 多路复用 相信很多人第一时间回答出来上面这些&#xff0c;那么面试官…

从开发角度读懂公司卫生间一直有人窜稀的原理

不知道你有没有这样的经历&#xff0c;一直忙忙碌碌&#xff0c;需求不断&#xff0c;当你终于解决完手头的事情&#xff0c;突然特别着急想要去卫生间的时候&#xff0c;仅有的几个门总是关着的&#xff0c;于是怀疑&#xff0c;可能其他人更需要这次机会&#xff0c;他们也一…

最基础的协同过滤介绍

文章目录1.到底什么是协同过滤2.协同过滤的一般步骤3.基于用户的CF (User-CF)3.1 基本介绍3.2 用户相似度3.2.1 用户相似度基本介绍3.2.2 用户相似度改进&#xff1a;ICU3.3 User-CF的缺点4.基于项目的CF (Item-CF)4.1 基本介绍4.2 用户相似度4.2.1 用户相似度基本介绍4.2.2 用…

xss-labs/level6

我们输入平常的payload如下 <script>alert(xss)</script> 界面回显如下 源代码如下所示 可以发现后台服务器对特殊字符进行插入操作 我们寄希望于后台不要插入下划线到onxxx关键字中 所以构造如下 " onclickjavascript:alert(xss)>// 由于界面没有回…

教程七 在Go中使用Energy创建跨平台GUI - Cookies

教程-示例-文档 介绍 本文介绍在energy中的cookie操作 在energy中可以对cookie的增加、修改和删除以达到某种目的 对cookie操作时&#xff0c;是以调用功能函数后触发事件的方式返回调用功能函数的结果 运行此示例&#xff0c;需要安装好Go和Energy开发环境&#xff1a;教…

有问题直接说问题,问什么在不在???

有什么问题可以直接说&#xff0c; 比如把你的项目地址、需求、错误复现步骤通过任何方式告诉我 比如&#xff1a; CSDN&#xff1a;发送消息&#xff0c;博客评论等&#xff0c;禁止发"在吗" 发邮件&#xff1a;xuxiaoweixuxiaowei.com.cn。 微信群&#xff1a; 禁…

麦芽糖-紫杉醇maltose-Paclitaxel

麦芽糖-紫杉醇maltose-Paclitaxel 中文名称&#xff1a;麦芽糖-紫杉醇 英文名称&#xff1a;maltose-Paclitaxel 别称&#xff1a;紫杉醇修饰麦芽糖&#xff0c;PTX-麦芽糖 还可以提供PEG接枝修饰麦芽糖&#xff0c;麦芽糖-聚乙二醇-紫杉醇,Paclitaxel-PEG-maltose,紫杉…

redis配置文件详解

一、概述 redis的配置文件中&#xff0c;有着许多说明和可配置项&#xff0c;了解它们能够更好的使用redis去解决开发中遇到的困难。 此配置文件基于linux下的redis-6.2.4版本。 二、单位换算描述-units 在配置文件开头就有这么一段&#xff1a; 这里描述了一些基本的度量…

信息流广告投放的技巧

随着互联网的发展&#xff0c;信息时代已经到来&#xff0c;信息流广告逐渐受到众多广告主的青睐。 做广告&#xff0c;不仅需要投入大量的精力&#xff0c;还需要一定的资金&#xff0c;花这么多&#xff0c;自然是要收获不错的收益。广告主在广告信息流的时候都追求高ROI&am…

蓝牙耳机什么牌子好?安卓蓝牙耳机性价比推荐

戴耳机所听到的音效是完全不同的&#xff0c;体验感也是完全不一样&#xff01;而有线的耳机戴起来一点也不方便&#xff0c;因此更多的人选择无线蓝牙耳机&#xff0c;但是很多新手发帖不知道蓝牙耳机啥牌子好&#xff1f;安卓手机的配置越来越好&#xff0c;深受大众的欢迎&a…

Python中的dump() 、load()和dumps()、loads()使用及示例

Python中的dump() 、load()和dumps()、loads() 结论&#xff1a; 1.不加s的标识对json文件的读写&#xff0c;将内存中值读取写入到json后缀文件&#xff0c;或者将json后缀文件中内容读取到内存 2.加了s的主要是字符串类型和其他数据类型的互转 JSON(JavaScript Object No…

KubeSphere 社区双周报 | KubeKey v3.0.2 发布 | 2022-11-24

KubeSphere 从诞生的第一天起便秉持着开源、开放的理念&#xff0c;并且以社区的方式成长&#xff0c;如今 KubeSphere 已经成为全球最受欢迎的开源容器平台之一。这些都离不开社区小伙伴的共同努力&#xff0c;你们为 KubeSphere 提出了很多建设性意见&#xff0c;也贡献了很多…

扫除知识共享障碍,天翎知识文档管理系统+群晖NAS一体化解决方案

编者按&#xff1a;知识共享是企业知识管理的重要模块&#xff0c;本文阐述了企业知识共享中常遇到的障碍&#xff0c;并指出知识文档管理系统可以帮助解决这些问题&#xff0c;而NAS与知识文档系统的结合更是锦上添花。 关键词&#xff1a;文档分类&#xff0c;版本管理&…