带你认识线程

线程的概念

前言: 一个程序运行起来,就会对应一个进程,例如,启动一个 Java 程序,就会创建一个 Java 进程。进程也被称为系统分配资源的基本单位。

一个进程可以包含一个线程,也可以包含多个线程(不能包含0个线程);线程是系统调度执行的基本单位,也被称为轻量级(Lignt Weight Process)


创建线程

在这里插入图片描述关于线程的创建,各位亲们,请详见博主的另一篇博客《Java 中创建线程(Thread)的五种方法》



使用多线程的原因

主要原因

  1. 单核CPU的发展遇到了瓶颈,要想提高算力,就需要多核CPU,而并发编程能更充分利用多核CPU资源,将计算逻辑分配到多个处理器核心上,显著减少程序的运行时间,并且随着更多处理器核心的加入而变得更加高效
  2. 有些任务场景需要"等待IO",为了让等待IO的时间能够去做一些其他的工作,也需要用到并发编程。
    理解: 就比如一些复杂的业务逻辑,当你在京东App上购物时,你购买一个东西时就涉及到了一笔订单的创建:插入订单数据,生成订单快照,发送邮件通知卖家和记录货品销售数量等。作为用户的你,从确认订购开始,就要等待上述的操作全部完成才能看到订购成功的结果。
    解决: 使用多线程技术,将比如生成订单快照,发送邮件等数据一致性不强的操作派发给其它的线程,就可以显著缩短响应时间,从而提示用户购物体验

次要原因: 虽然多进程也能实现并发编程,但是线程比进程更轻量,同一个进程的线程之间,共享了资源 ( 内存+文件描述符表等)
   体现如下:
   1.创建线程比创建进程更快;
   2.销毁线程比销毁进程更快;
   3.调度线程比调度进程更快;


进程的状态

Java 线程的状态

状态名称说明
NEWThread 对象有了,但还没调用 start(),系统内部的线程还未创建
RUNNABLE就绪状态:指的是这个线程“随叫随到”
以下 2 种情况:
1. 这个线程正在 CPU 执行
2. 这个线程虽然没在 CPU 执行,但是随时可以调度到 CPU 上执行
TERMINATED线程已经终止了,内核中的线程已经销毁了,但是 Thread 对象还在
WAITING阻塞:死等进入的阻塞
TIMED_WAITING阻塞:在进行带有超时时间的等待
BLOCKED阻塞:进行锁竞争的时候产生的阻塞

注:只要线程出现WAITINGTIMED_WAITINGBLOCKED 中的任意一个状态,都是阻塞,只是产生这几个状态的原因不一样


作图理解:

在这里插入图片描述

启动线程

线程对象在初始化完成之后,调用 start() 方法就可以启动。
线程 start() 方法的含义是:当前线程同步告知 Java 虚拟机,只要线程规划器空闲,应立即启动调用 start() 方法的线程。
调用 start 方法,才真的在操作系统的底层创建出一个线程


中断/终止线程

中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。

终止线程,在Java中 都只是"提醒,建议",真正要不要终止,还得线程本体来进行决定的!!
t 线程,正在执行,其他线程,只能提醒一下t是不是要终止了,t 收到这样的提醒之后,也还是得自己决定的。终止就好比其他线程对该线程打了一个招呼

停止线程的常见方式有以下2种:

  1. 通过共享的标记来进行沟通
  2. 调用 interrupt() 方法来通知

示例1: 使用自定义的变量来作为标志位
核心思路:让需要终止的线程的入口方法尽快执行结束(跳出循环,还是尽快return都可以)
代码示例:

package Thread;/*** Created with IntelliJ IDEA.* Description:* User: fly(逐梦者)* Date: 2024-03-29* Time: 10:44*/
public class Demo10 {private static boolean isRunning = true;public static void main(String[] args) {Thread t = new Thread(()-> {while (isRunning){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t 线程已经结束了");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 3s 之后,主线程修改 isRunning 的值,从而通知 t 结束System.out.println("控制 t 线程结束");isRunning = false;}
}

这里的isRunning不能设置为局部变量,只能设置为静态成员变量

原因: 涉及到变量捕获,作为lambda或者匿名内部类,都能捕获到外面一层作用域中的变量名,就可以使用的。
但是变量捕获,有一个前置条件,就是要求变量得是final(常量)或者“事实" final",isRunning变量涉及到修改操作,变量捕获失败,javac 无法编译通过,编译器会给你爆红提示报错;
设置为静态成员变量就不是变量捕获了,而是内部类访问外部类的成员,此时 lambda 本质上就是一个匿名内部类,实现了函数式接口

上述代码的缺点: 有些情况下,main 线程是无法及时把 t 线程终止掉

代码运行结果:

在这里插入图片描述


示例2: 使用 Thread.interrupted() 代替自定 义标志位
刚才是定义了一个boolean变量,实际上Thread 里面内置了一个,使用内置的标志位,功能要更强大
代码示例:

package Thread;/*** Created with IntelliJ IDEA.* Description:* User: fly(逐梦者)* Date: 2024-03-29* Time: 11:19*/
public class Demo11 {public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {
//                    throw new RuntimeException(e);e.printStackTrace();}}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}t.interrupt();}
}

Thread.currentThread() => 是个static方法,这个方法,能够获取到当前线程,可以能够获取到 t 这个引用
isInterrupted() => 线程内置的标志位,boolean 类型变量,true 表示线程要终止了,false表示线程要继续执行
t.interrupt() => 这个方法,相当于示例1的设置 boolean 值为 true,并且除了能设置boolean值,还可以唤醒 sleep等阻塞的方法。即使正在sleep(10s),刚休眠1s按照示例1/第一种写法,必须再等待9s,才能让线程结束(sleep结束了,才能继续进行循环判定); 示例2/第二种写法,则立即就会让 sleep抛出一个InterruptedException异常,不会再等待,立即就唤醒了

当使用 Interrupt 方法之后,此时,要不要结束,都是t线程自己决定的!!!

代码运行结果:
在这里插入图片描述


现象: sleep 在抛出异常后,没有立即终止,而是继续循环打印。
原因: 出现这个现象,还是 sleep在搞鬼;
如果代码没有sleep,确实是直接修改了标志位就完了;
如果有 sleep,并且是触发 Interrupt 的时候,线程正在 sleep,sleep 被唤醒的同时,就会清除刚才的标志位(又改回false );
之所以要改回来,就是把控制权,转交给作为程序员的我们.

如果想立即结束,直接在 catch 语句中加个break, 即可。至于稍后结束,还是不想结束,稍微修改下代码就行了
相关代码:

    Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                    e.printStackTrace();break;}}});t.start();

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

线程等待

存在问题的代码:

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

运行结果:
在这里插入图片描述
分析原因

根本原因: 多个线程,在系统中的调度顺序,是无序的(抢占式执行),作为程序猿的我们,期望程序的结果是稳定的,不应该是"随机"的
希望在随机的体系上,加入一些控制,让结果变的不那么随机

问题: 多个线程什么时候被调度执行,不确定 => 多个线程谁先执行结束,不确定

解决: 通过线程等待(join),来确定线程结束的先后顺序

join 相关函数

函数返回值方法及其作用个人理解
voidjoin()
Waits for this thread to die.
这个join无参数版本 (很少使用)
死等,不见不散
只要 t 不结束,join 就会一直等待下去
voidjoin(long millis)
Waits at most millis milliseconds for this thread to die.
(常用)等待 N 毫秒
比如,写了等待10ms,如果10ms之内, t 线程结束了,直接返回
如果10ms 到了,t 还没结束,不等了,代码继续往下执行了
voidjoin(long millis, int nanos)
Waits at most millis milliseconds plus nanos nanoseconds for this thread to die.
等待 N 毫秒 又 M 纳秒 (几乎不用)

具体实现: 由于上述代码中, main 和 t 线程,之间的结束顺序是不确定的,如果希望让代码里面的t能够先结束, main后结束,就可以在main 中使用线程等待(join)

代码如下:

package Thread;/*** Created with IntelliJ IDEA.* Description:* User: fly(逐梦者)* Date: 2024-03-30* Time: 2:06*/
public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0; i < 3; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();for (int i = 0; i < 3; i++) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}t.join();System.out.println("main end");}
}

运行结果:

在这里插入图片描述

main 中调用上述 join 方法,有以下两种可能:
1.如果t线程此时已经结束了,此时join 就会立即返回 => 没涉及到任何阻塞操作直接往下执行了
2.如果t线程此时还没结束,此时 join 就会 阻塞 等待,一直等待到t线程结束之后, join才能解除阻塞,继续执行 => 这就确保了 main 线程—定是后结束
阻塞: 该线程暂时不参与cpu调度执行,解除阻塞继续执行,线程重新参与到cpu调度了

注意:可怜的main线程 -> main 一般情况都不会被其它线程 join,即是不会被等待的 ~~ 批准:main 你什么时候才能摆脱舔狗的身份啊!!!


线程安全

批注:(#^.^#) 浅谈一下,接下来的博客会详细的讲讲多线程带来的线程安全

线程安全问题的原因:
1.抢占式执行,随机调度
2.多个线程同时修改同一个变量
3.修改操作不是原子的
4.内存可见性
5.指令重排序

解决线程安全问题: 加锁(synchronized)

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

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

相关文章

政安晨:【Keras机器学习实践要点】(九)—— 保存、序列化和导出模型

目录 介绍 如何保存和加载模型 保存一个Keras模型 装回模型 设置 保存 例子&#xff1a; 自定义对象 向 load_model() 传递自定义对象 使用自定义对象范围 模型序列化 APIs 内存模型克隆 任意对象序列化和反序列化 保存模型权重 内存中的权重传递接口 无状态层…

新能源充电桩站场视频汇聚系统建设方案及技术特点分析

随着新能源汽车的普及&#xff0c;充电桩作为新能源汽车的基础设施&#xff0c;其安全性和可靠性越来越受到人们的关注。为了更好地保障充电桩的安全运行与站场管理&#xff0c;TSINGSEE青犀&触角云推出了一套新能源汽车充电桩视频汇聚管理与视频监控方案。 方案采用高清摄…

甲骨文护城河(MOAT)分析工具-用户指南

甲骨文护城河&#xff08;MOAT&#xff09;分析工具-用户指南 登录后&#xff0c;您可以通过显示的基于web的用户界面访问Moat Analytics Dashboard。 以下是如何通过UI使用护城河的指南的使用目录&#xff1a; 一、主屏幕导航 在面板中创建警报和导出 二、无效流量概述 什…

深入浅出MHA(MySQL Master High Availability)集群:原理、部署与实践

目录 引言 一、MHA集群介绍 &#xff08;一&#xff09;什么是MHA &#xff08;二&#xff09;MHA集群原理 &#xff08;三&#xff09;同步方式 &#xff08;四&#xff09;管理节点与数据节点 二、实现MHA &#xff08;一&#xff09;搭建主从复制环境 1.搭建时间同…

Github profile Readme实现小游戏[github自述游戏]

Github profile Readme常用于个人主页介绍&#xff0c;将它与action自动化流程结合&#xff0c;可以实现一些小游戏 例如&#xff1a;2048、五子棋 2048实现 losehu (RUBO) GitHub 五子棋 https://github.com/losehu/losehu/tree/main 通过python/C编写可执行文件&#xf…

智能网关BL102E采集西门子PLC S7-200 Smart数据上传至Thingsboard

1、WAN口采集西门子PLC的配置 WAN口可以添加很多设备,具体我们用西门子为例来简要配置。 双击WAN,弹出以太网设置,直接把自动获取IP打开,他会根据你的网段自动设置链接! (1)点击`WAN",点击鼠标右键,点击“添加",弹出设备配置框。 (2)设备名称任意填写,如…

《数据结构学习笔记---第七篇》---栈和队列的OJ练习

1. 括号匹配问题。OJ链接 step1:思路分析 &#xff1a; 1.括号匹配&#xff0c;我们首先考虑用栈实现&#xff0c;我们通过符号栈帧的思想知道&#xff0c;求前中后缀表达式的时候用的就是栈帧&#xff0c;操作数栈和符号栈。 2.根据常见的情况 考虑怎么使用栈&#xff0c;首先…

Bun安装与使用

Bun安装与使用。 它目前无法在windows上直接安装使用&#xff0c;必须通过虚拟机安装。 在win10虚拟机中安装 # 查看内核版本 $ uname -srm Linux 6.1.0-10-amd64 x86_64# 安装unzip解压工具 $ sudo apt install unzip# 下载安装脚本并开始安装 curl -fsSL https://bun.sh/ins…

SpringBoot使用Redis

1.Spring是如何集成Redis的&#xff1f; Spring Data Redis 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId&…

互联网轻量级框架整合之JavaEE基础

不得不解释得几个概念 JavaEE SUN公司提出来的企业版Java开发中间件&#xff0c;主要用于企业级互联网系统的框架搭建&#xff0c;同时因为Java语言优质的平台无关性、可移植性、健壮性、支持多线程和安全性等优势&#xff0c;其迅速成为构建企业互联网平台的主流技术&#x…

如何使用Docker搭建WBO在线协作工具并实现无公网IP远程编辑本地白板

文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cpolar4. 配置WBO公网访问地址5. 公网远程访问WBO白板6. 固定WBO白板公网地址 前言 WBO在线协作白板是一个自由和开源的在线协作白板&#xff0c;允许多个用户同时在一个虚拟的大型白板上画图。该白板对所有线上用…

社交互动:探讨Facebook对用户互动的影响

在当今数字化时代&#xff0c;社交网络已经成为了人们日常生活中不可或缺的一部分。而作为最著名的社交网络平台之一&#xff0c;Facebook不仅连接了全球数十亿用户&#xff0c;还对用户的社交互动产生了深远的影响。本文将深入探讨Facebook对用户互动的影响&#xff0c;以及它…

5、axios请求、动画、组件、路由重定向、UI组件

一、axios请求 Axios是一个基于Promise的HTTP状态库&#xff0c;封装ajax。ajax包含axios安装 npm install axios 引入 import axios form “axios” 1、get请求 <script> // 1.本页面引入 import axios from "axios";data() {return {imgSrc: ""…

R语言实现——网状 Meta 分析

近来年&#xff0c;网状 Meta 分析相关研究不断涌现&#xff0c;此类研究不但能发表在国内各大核心期刊上&#xff0c;还能在SCI期刊甚至医学4大刊上看到其身影。随手在pubmed上面一搜索&#xff0c;就能得到一万多篇相关文献。俨然成为医学文献研究的“大杀器”&#xff01; P…

AMEYA360代理 | 江苏长晶科技FST2.0高性能 IGBT产品介绍

江苏长晶科技股份有限公司是一家专业从事半导体产品研发、生产和销售的企业。自2019年起&#xff0c;连续4年被中国半导体行业协会评为 “功率器件十强企业”。2021年开始自主研发有着“工业CPU”之称的IGBT&#xff0c;截至2023年Q3在家电/工业/新能源等行业实现8款产品市场应…

刷题DAY37 | LeetCode 738-单调递增的数字 968-监控二叉树

738 单调递增的数字&#xff08;medium&#xff09; 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 思路&#xff1a;将整数转…

Git版本管理使用手册 - 6 - 将本地项目提交到空白仓库

将本地项目提交到空白仓库 1.首先克隆远程空白仓库到本地目录 2.将要提交到master上的项目代码复制到本地仓库目录下。如果项目代码关联SVN要取消SVN关联。可以使用取消SVN关联脚本。 3.编写.ignore文件&#xff0c;该文件可以提交时&#xff0c;忽略指定文件 4.使用idea打开该…

Docker搭建LNMP环境实战(08):安装php-fpm

1、编写php测试文件 在文件夹&#xff1a;/mnt/hgfs/dockers/test_site/www目录下创建文件&#xff1a;test.php&#xff0c;内容为&#xff1a; <?phpecho "hello world!!!!!! From test.php"; ?>2、编写php-fpm部署配置文件 在文件夹&#xff1a;/mnt/h…

【unity】unity安装及路线图

学习路线图 二、有关unity的下载 由于unity公司是在国外&#xff0c;所以.com版&#xff08;https://developer.unity.cn/&#xff09;不一定稳定&#xff0c;学习时推荐从.cn国内版&#xff08;https://developer.unity.cn/&#xff09;前往下载&#xff0c;但是后期仍需回…

鸿蒙ArkTS基础知识-自定义组件生命周期函数onBackPress的调用。

介绍 本课程使用声明式语法和组件化基础知识&#xff0c;搭建一个可刷新的排行榜页面。在排行榜页面中&#xff0c;使用循环渲染控制语法来实现列表数据渲染&#xff0c;使用Builder创建排行列表布局内容&#xff0c;使用装饰器State、Prop、Link来管理组件状态。最后我们点击…