多线程的学习第二篇

previewfile_28144258300213

多线程

线程是为了解决并发编程引入的机制.
线程相比于进程来说,更轻量
~~ 更轻量的体现:

  • 创建线程比创建进程,开销更小
  • 销毁线程比销毁进程,开销更小
  • 调度线程比调度进程,开销更小

进程是包含线程的.
同一个进程里的若干线程之间,共享着内存资源和文件描述符表

每个线程被独立调度执行.每个线程都有自己的状态/优先级/上下文/记账信息

进程是操作系统资源分配的基本单位
线程是操作系统调度执行的基本单位


Thread类 ~~ 五种创建线程的写法

1.继承Thread,重写run
2.实现Runnable,重写run
3.使用匿名内部类,继承Thread
4.使用匿名内部类,实现 Runnable
5.使用lambda表达式

使用了不同的方式来描述, Thread里的任务是什么.
上述办法,只是语法规则不同,本质上都是一样的方式.
~~ 这些方法创建出来的线程,都是一样的.

对Thread的run方法和start方法的区别
run ~~ 描述了线程要做的工作.
start ~~ 真的在操作系统内核里弄了个线程,并且让新线程调用run方法.


Thread的用法

Thread 的常见构造方法

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

Thread(Runnable target, String name)
Thread(String name)
这2个方法的参数String name
=> 给线程起个名字 ~~ 为了方便调试
线程默认的名字,叫做thread-0之类的…
thread-1, 2, 3
为了加深印象,写个线程名字为myThread的代码,通过 JConsole 观察线程的名字

public class ThreadDemo6 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("hello");}}},"myThread");t.start();}
}

image-20230920004942382

这图中没有主线程的存在 ~~ 主线程执行完了start之后,紧接着就结束了main方法.
对于主线程来说, main方法完了,自己也就没了.
myThread线程也是如此,run方法执行完了,此时线程也就没了.
图示:
image-20230920010312996

两个线程在微观上,可能是并行(两个核心),也可能是并发的(一个核心)
宏观上感知不到(应用程序这里),咱们看到的始终是随机执行,抢占式调度…
操作系统决定到底怎么使用CPU, 我们是不知道的.

Thread的几个常见属性

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

getId(): 获取到线程的ID, ID 是线程的唯一标识,不同线程不会重复
getName(): 获取到构造方法里的名字
getState(): 获取到线程状态, 状态表示线程当前所处的一个情况(Java里线程的状态要比操作系统原生的状态更丰富一些)
getPriority(): 优先级,这个不仅可以获取,还可以进行设置,优先级高的线程理论上来说更容易被调度到 ,但是呢,实际上起不到什么作用!!!
isDaemon(): 是否是“守护线程”,也可以叫做是“后台线程” ~~ 相对应的还存在“前台线程”这个概念.
~~ 玩手机的时候,手机上一般都会运行多个APP,在运行多个APP的时候,你打开微信,来聊天,此时微信就处在‘’前台‘’的状态.然后,你想打游戏,就切到王者荣耀的游戏界面.这时,微信就跑到了‘’后台‘’.
前台线程: 会阻止进程结束, 前台线程的工作没做完, 进程是不会结束的
后台线程: 不会阻止进程结束, 后台线程工作没做完, 进程也是可以结束的
代码里手动创建的线程,默认都是前台线程,包括 main 默认也是前台的.
其他的 JVM 自带的线程都是后台的
~~ 也可以使用 setDaemon 设置成后台线程.
是“后台线程”,就是‘’守护线程‘’.

注:要把线程是否是后台,和线程调度切换,区分开,两者不是一回事.
线程的后台前台,只是取决于你调用的setDaemon这个方法做了什么…
image-20230920132218590

isAlive(): 在真正调用start之前,调用t.isAlive 就是 false.调用start之后, isAlive就是 true.
在真正调用start之前,调用t.isAlive 就是 false.调用start之后, isAlive就是 true.
~~ 另外,如果内核里线程把 run千完了,此时线程销毁, pcb随之释放,
但是 Thread t这个对象还不一定被释放的,此时isAlive 也是 false.

image-20230920184520089

Thread t这个对象还不一定被释放的,此时isAlive 也是 false的例子:
image-20230920191341931

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

时刻牢记!!线程之间是并发执行的,并且是抢占式调度的.

image-20230920195527226

线程调度,就好比,顾客们去餐馆吃饭一样.
如果把CPU 想象成餐馆,每个线程就是来餐馆吃饭的人
假设ABCD这四个人是天天来的常客 工
但你无法确定,今天来的这四个人的顺序是ABCD,还是DCBA,还是BADC……
由于每个人的行为之间都是独立的.A这个来的时间和BCD是没关系的.
即使假设,按照ABCD顺序来了.如果这四个人来的时间差很多,是一个接一个来的,此时也无法保证,上菜的顺序也是ABCD……. => 充满了完全不可预期的因素

以前写代码,只要读懂代码的顺序,就是固定按照从上到下的顺序来执行的…
但是现在不是了,理解多线程代码,要考虑无数种顺序.

中断一个线程

中断的意思是,不是让线程立即就停止,而是通知线程,你应该要停止了.
是否真的停止,取决于线程这里具体的代码写法.
例子: 暑假在家的时候,你正在打王者,突然,你妈喊你下楼去买包盐,
你的选择如下:
1.放下游戏,立即就去.
2.打完这把,再去,稍后处理.
3.假装没听见,就完全不处理.
不同的选择,就对应不同的结果

停止线程的方式

目前常见的有以下两种方式:

1.使用标志位来控制线程是否要停止.

示例代码

public class ThreadDemo8 {public static boolean flag = true;public static void main(String[] args) {Thread t = new Thread(()->{while (flag){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}//在主线程里就可以随时通过 flag变量的取值,来操作t线程是否结束.flag = false;}
}

运行结果

image-20230920214459036

这个代码之所以能够起到,修改flag, t 线程就结束
=> 完全取决于t 线程内部的代码.代码里通过flag控制循环.
这个线程是否要结束,啥时候结束都是线程内部自己代码来决定的.
缺陷
自定义变量这种方式不能及时响应.尤其是在sleep 休眠的时间比较久的时候

2.使用Thread自带的标志位进行判定

~~ 它是可以唤醒上面的这个sleep这样的方法的.
示例代码

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread t =new Thread(()->{while (!Thread.currentThread().isInterrupted()){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();Thread.sleep(3000);t.interrupt();}
}

!Thread.currentThread().isInterrupted()
它是Thread类的静态方法,通过这个方法可以获取到当前线程.
哪个线程调用的这个方法,就是得到哪个线程的对象引用. ~~ 很类似于this
在博主所写的上述代码中,是在t.run中被调用的.此处获取的线程就是t线程.

isInterrupted() ~~ 前面加了个‘!,表示逻辑取反.
为true表示被终止.
为false表示未被终止.(应该要继续走)

t.interrupt(); => 终止 t 线程

​ 注: isInterrupted() => 这个方法背后,就相当于是判定一个boolean变量
t.interrupt() => 就是在设置这个 boolean 变量

​ main 线程调用 t.interrupt() 相当于main通知 t 线程你要终止了.

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位

image-20230920234340825

image-20230921000821986

为啥 sleep要清除标志位?
唤醒之后,线程到底要终止,还是不要到底是立即终止还是稍后,就把选择权
交给程序猿自己了!!!

等待一个线程

~~ join()
线程是一个随机调度的过程.
等待线程,做的事情,就是在控制两个线程的结束顺序.
示例代码

public class ThreadDemo10 {public static void main(String[] args) {Thread t = new Thread(()->{for (int i = 0; i < 3; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();System.out.println("join 之前");try {t.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("join 之后");}
}

运行结果

image-20230921004505244

画图对代码进行解释:

image-20230921004425415

主线程,等待 t 线程彻底执行完毕之后,才继续往下执行了

假设开始执行join的时候, t 线程已经结束了,此时join 会咋样?
如果是执行join的时候, t 线程已经结束了join不会阻塞,就会立即返回 => 保证线程的结束顺序

方法说明理解
public void join()等待线程结束无参数版本,“死等”,不见不散
public void join(long millis)等待线程结束,最多等 millis 毫秒指定一个超时时间(最大等待时间) ~~ 这种操作方式是更常见的 ~~ 死等很容易出问题
public void join(long millis, int nanos)同理,但可以更高精度不做注释

文章总结 ❤️❤️❤️

Thread 类
标准库提供的,用来操作线程的类

  1. 创建线程
  2. Thread的一些属性和方法
  3. 终止线程
    isInterruptted => 判定, interrupt => 中断
    线程 A 只是通知线程 B 是否要终止, B 线程是否要终止, 看 B 线程有关代码的写法!!!
    为什么不设计成 A 让 B 终止, B 就立即终止呢?
    => A,B线程之间是并发执行的,随机调度的,导致B这个线程执行到哪里了, A线程是不知道的.
  4. 等待线程 ~~ join
    在线程t1中,调用线程t2.join,让线程t1等待线程t2结束,此时线程t1就会进入阻塞等待的状态.

知识扩展

多线程是实现并发编程的基础方式.比较接近系统底层,使用起来不太友好
为了简化并发编程,也引入了很多更方便,更不容易出错的写法
比如,erlang(编程语言),引入actor 模型.
go,引入了csp模型
js, 使用回调的方式 => async await
python => async await
……

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

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

相关文章

外国固定资产管理系统功能有哪些

很多公司都在寻找提高自己资产管理效益的方法。为了满足这一要求&#xff0c;国外的固定资产管理系统已经发展成多种形式。以下是国外一些常见的固定资产管理系统的特点:自动化和智能化:许多现代固定资产管理系统采用自动化和数字化技术&#xff0c;以简化流程&#xff0c;减少…

网络编程day04(网络属性函数、广播、组播、TCP并发)

今日任务 对于newfd的话&#xff0c;最好是另存然后传入给分支线程&#xff0c;避免父子线程操作同一个文件描述符 1.广播&#xff1a; 接收端 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h&…

Databend 开源周报第 111 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 理解 SHARE END…

大数据(九):数据可视化(一)

专栏介绍 结合自身经验和内部资料总结的Python教程&#xff0c;每天3-5章&#xff0c;最短1个月就能全方位的完成Python的学习并进行实战开发&#xff0c;学完了定能成为大佬&#xff01;加油吧&#xff01;卷起来&#xff01; 全部文章请访问专栏&#xff1a;《Python全栈教…

Linux_9_网络协议和管理

目录 1网络基础1.1 网络概念1.2常见的网络物理组件1.3网络应用程序1.3.1各种网络应用1.3.2应用程序对网络的要求 1.4网络的特征1.4.1速度(带宽)1.4.2网络拓扑 1.5网络1.5.1网络准和分层1.5.2开放系统互联OSI1.5.3网络的通信过程1.5.3.1数据封装和数据解封1.5.3.2协议数据单元PD…

基于javaweb的顶岗实习管理系统(jsp+servlet)

系统简介 本项目采用eclipse工具开发&#xff0c;jspservletjquery技术编写&#xff0c;数据库采用的是mysql&#xff0c;navicat开发工具。 三个角色&#xff1a;管理员&#xff0c;教师&#xff0c;学生 模块简介 管理员&#xff1a; 1、登录 2、学生管理 3、公告管理 …

如何通过一键导出导入数据实现批量重命名文件名称

在日常办公中&#xff0c;我们经常需要对大量的文件进行重命名&#xff0c;以便更好地管理和查找文件。而且&#xff0c;有时候我们还需要将文件名称翻译成其他语言&#xff0c;以适应不同的工作需求。如何高效地完成这项任务呢&#xff1f;接下来&#xff0c;我将介绍一种方法…

【李沐深度学习笔记】线性代数实现

课程地址和说明 线性代数实现p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 这节就算之前内容的复习&#xff0c;后面以截图形式呈现 标量由只有一个元素的张量表示 import torch x torch.tensor([3.0]) y …

Spring Boot魔法:简化Java应用的开发与部署

文章目录 什么是Spring Boot&#xff1f;1. 自动配置&#xff08;Auto-Configuration&#xff09;2. 独立运行&#xff08;Standalone&#xff09;3. 生产就绪&#xff08;Production Ready&#xff09;4. 大量的起步依赖&#xff08;Starter Dependencies&#xff09; Spring …

免杀对抗-java语言-shellcode免杀-源码修改+打包exe

JAVA-ShellCode免杀-源码修改&打包EXE Shellcode-生成/上线 1.msf生成shellcode 命令&#xff1a;msfvenom -p java/meterpreter/reverse_tcp LHOSTx.x.x.x LPORTxxxx -f jar -o msf.jar 2.msf设置监听 3.执行msf生成的shellcode jar包&#xff0c;成功上线 命令&#xff1…

05_Bootstrap插件02

7 小标签 通过 .label 实现小标签&#xff0c;用于提示类。 <h1>h1标题 <span class"label label-default">标签</span></h1> <h2>h2标题<span class"label label-default">标签</span></h2> <h3&g…

Uni-app 调用微信地图导航功能【有图】

前言 我们在使用uni-app时&#xff0c;有时候会遇到需要开发地图和导航的功能&#xff0c;这些方法其实微信小程序的API已经帮我们封装好了 详见&#xff1a;微信小程序开发文档 接下来我们就演示如何用uni-app来使用他们 使用 <template><view><button type…

5G通信与蜂窝模组之间的关系

5G通信是第五代移动通信技术的简称&#xff0c;它代表了一种新一代的无线通信技术标准。5G通信的主要目标是提供更高的数据传输速度、更低的延迟、更大的网络容量以及更可靠的连接&#xff0c;以支持各种新兴应用和服务&#xff0c;包括高清视频流、虚拟现实、物联网&#xff0…

Mybatis学习笔记11 缓存相关

Mybatis学习笔记10 高级映射及延迟加载_biubiubiu0706的博客-CSDN博客 缓存:cache 缓存的作用:通过减少IO的方式,来提高程序的执行效率 Mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库.一方面是减少了I…

【Vue3 源码讲解】nextTick

nextTick 是 Vue 3 中用于异步执行回调函数的函数&#xff0c;它会将回调函数延迟到下一个微任务队列中执行。其中&#xff0c;Vue 更新 DOM 是异步的。下面是对 nextTick 函数的详细解释&#xff1a; export function nextTick<T void, R void>(this: T,fn?: (this:…

websocket php教程

WebSocket 是 HTML5 提供的一种网络通讯协议&#xff0c;用于服务端与客户端实时数据传输。广泛用于浏览器与服务器的实时通讯&#xff0c;APP与服务器的实时通讯等场景。 相比传统HTTP协议请求响应式通讯&#xff0c;WebSocket协议可以做到实时的双向通讯&#xff0c;服务端可…

Webpack打包图片

一、在js文件中引入图片 二、在package.config.js中配置加载器 module.exports {mode: "production", // 设置打包的模式&#xff1a;production生产模式 development开发模式module: {rules: [// 配置img加载器{test: /\.(jpg|png|gif)$/i,type:"asset/resou…

Hadoop-sqoop

sqoop 1. Sqoop简介及原理 简介&#xff1a; Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysq1.postgresql..)间进行数据的传递&#xff0c;可以将一个关系型数据库&#xff08;例如: MySQL ,Oracle ,Postgres等&#xff09;中的数据导进到Hadoop 的HDFS中&…

虚拟机中window/ubuntu系统如何联网?

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 &#xff08;1&#xff09;VMware虚拟机中Windows11无法连接网络 &#xff08;2&#xff09;图解vmware虚拟机win8无线上网 &#xff08;3&#xff09;VMware中VMnet0、VMnet1、VMnet8是什么 &…

python教程:使用gevent实现高并发并限制最大并发数

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 import time import gevent from gevent.pool import Pool from gevent import monkey # 一&#xff0c;定义最大并发数 p Pool(20) # 二&#xff0c;导入gevent…