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

JavaEE初阶Day 3:多线程(1)

目录

  • Day 3:多线程(1)
    • 1. 线程
      • 1.1 引入线程的原因
      • 1.2 线程的定义
      • 1.3 为何线程更轻量
      • 1.4 问题
    • 2. 多线程代码
      • 2.1 继承Thread重写run
      • 2.2 通过实现Runnable接口创建线程
      • 2.3 针对2.1的变形使用匿名内部类
      • 2.4 针对Runnable创建匿名内部类
      • 2.5 使用lambda表达式

Day 3:多线程(1)

C++会对进程有更进一步介绍,例如:如何通过编写代码,来进行进程的控制(多进程编程),但是Java并不太关注这些

  • JVM没有提供上述多进程编程的api
  • Java生态中也不太鼓励使用多进程编程

JVM也不是完全没有,也提供了非常粗糙的多进程操作的api,但是控制过程不如C++通过系统原生api更精细

1. 线程

1.1 引入线程的原因

当前的CPU都是多核心CPU,需要通过一些特定的编程技巧,把要完成的任务,拆解成多个部分,并且分别让他们在不同的CPU核心上运行,也就是**“并发编程”**

  • 通过多进程编程的模式,其实就可以起到“并发编程”的效果,因为进程可以被调度到不同的CPU上运行,此时就可以把多个CPU核心都给很好的利用起来,虽然,多进程编程可以解决上述问题,也带来了新的麻烦
  • 在服务器开发中,并发编程的需求场景非常常见,所以一个服务器要能够同时给多个客户端提供服务,如果同一时间,来了很多客户端,服务器如果只能利用一个CPU核心工作,速度就会比较慢
  • 一种典型的做法:每个客户端连上服务器了,服务器都创建一个进程,给客户端提供服务,这个客户端断开了,服务器再把进程给释放掉,如果这个服务器,频繁的有客户端来来去去,服务器就需要频繁创建/销毁进程

所以引入线程,来解决上述进程“太重量”的问题

1.2 线程的定义

线程(thread),也称为“轻量级进程”,创建和销毁的开销更小,线程可以理解成“进程的一部分”,一个进程中可以包含一个线程或者多个线程,描述进程,使用PCB这样的结构体,事实上,更严格地说,一个PCB其实是描述一个线程的,若干个PCB联合在一起,是描述一个进程的

PCB:pid(每个线程都不一样)、内存指针、文件描述符表、状态、上下文、优先级、记账信息、tgid(同一个进程的tgid是同一个)

同一个进程的若干个线程,是共用相同的内存资源和文件资源的,这里的内存指针和文件描述符表其实是同一个,但是每个线程都是独立在CPU上调度执行

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

引入线程后,就可以每个客户端分配一个线程来处理,起到优化效果

1.3 为何线程更轻量

为什么线程比进程更轻量/为什么说线程创建和销毁的开销比进程更小

核心在于,创建进程,可能要包含多个线程,这个过程中,涉及到资源分配/资源释放,创建线程,相当于资源已经有了,省去了资源分配/资源释放步骤了,同一个进程包含N个线程,这些线程之间是共用资源的,只有创建第一个线程(也是创建进程的时候),去进行资源申请操作,后续再创建线程,都没有申请资源的过程了

1.4 问题

  • 线程不能无限引入:总的线程越多,单位时间内要进行调度的次数也越多,调度消耗的系统资源自然就更多了,这个时候,线程调度开销就会非常明显,程序的性能可能不升反降
  • 线程安全问题:多个线程之间可能产生冲突
  • 如果一个线程抛出异常,并且没有很好的捕获处理好,就会使得整个进程退出,多线程编程值得关注的难点:一个线程出现问题,会影响到别的线程

2. 多线程代码

线程本身是操作系统提供的,操作系统提供了api让我们操作线程,JVM就对操作系统api进行了封装,Java中提供了Thread类,表示线程

2.1 继承Thread重写run

  • public void run():run只是描述了线程要干啥任务,run不是start调用的,是start创建出来的线程,线程里被调用的

  • t.start();Thread类中自带的方法,调用操作系统提供的“创建线程”api,在内核中创建对应的PCB,并且把PCB加入到链表中,进一步的系统调度到这个线程之后,就会执行上述run方法中的逻辑

像run这种方法,只是定义好,而不用去手动调用,把这个方法的调用,交给系统/其他的库/其他的框架(别人)调用这样的方法(函数)称为**“回调函数”**(callback function)

package thread;class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();for (int i = 0; i < 5; i++) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

上述代码中有两个线程

  • t 线程
  • main方法所在的线程(主线程):JVM进程启动的时候,自己创建的线程

JDK中包含了jconsole工具,通过这个工具可以更直观地看到内部进程的情况,里面除了main与t线程,剩下的线程,都是JVM帮我们做的一些其他工作,有的是负责垃圾回收的,有的是负责记录调试信息的

Thread.sleep(1000);:让线程主动进入“阻塞状态”,主动放弃去CPU上执行,时间到了之后,线程才会接触阻塞状态,重新被调度到CPU上执行,加上sleep就让CPU消耗的资源大幅度降低了,不加入sleep,消耗CPU资源将会特别大,while循环太快了

未来实际开发中,如果服务器程序消耗CPU的资源超出预期,如何排查

  • 先确认是哪个线程消耗的CPU比较高,未来会涉及到到第三方工具,可以看到每个线程的CPU的消耗情况,确定了之后,进一步排查,线程中是否有类似的“非常快速”的循环
  • 确认清楚,这里的循环是否应该这么快,如果应该,说明需要升级更好的CPU,如果不应该,说明需要在循环中引入一些"等待操作"(不一定是sleep)

上述代码补充说明

  • 每秒钟打印一次,每一秒打印的时候,可能是main在前面,也可能是thread在前面
  • 多个线程的调度顺序,是无序的,在操作系统内部称为**“抢占式执行”**,任何一个线程,在执行到任何一个代码的过程中,都可能被其他线程抢占掉它的CPU资源,于是CPU就给别的线程执行了
  • 这样的抢占式执行,充满了随机性,正是这样的随机性,使多线程程序的执行效果也会难以预测,甚至可能会引入bug
  • 主流的系统(Linux、Windows)都是属于这种实现方式,也有一些小众的系统(实时操作系统),通过“协商式”进行调度,虽然牺牲了很多功能,换来了调度的实时性

2.2 通过实现Runnable接口创建线程

  • Runnable的作用,是描述了一个“任务”,这个任务和具体的执行机制无关(通过线程的方式执行,还是通过其他的方式执行),run就是要执行的任务内容本身了

  • 引入Runnable就是为了解耦合,未来如果要更换其他的方式来执行这些任务,改动成本比较低,把任务内容和线程这个概念给拆分开了,这样的任务,就可以给其他的地方来执行

package thread;
class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo2 {public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();for (int i = 0; i < 5; i++) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}

2.3 针对2.1的变形使用匿名内部类

package thread;public class Demo3 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();for (int i = 0; i < 5; i++) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

此处的new Thread()

  • 创建了一个Thread的子类(不知道啥名字,匿名)
  • 同时创建了一个该子类的实例:对于匿名内部类来说,只能创建这一个实例,之后再也拿不到这个匿名内部类了
  • 此处的子类内部重写了父类的run方法

2.4 针对Runnable创建匿名内部类

package thread;public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();for (int i = 0; i < 5; i++) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

此处匿名内部类,只是针对Runnable,和Thread没有关系,只是把Runnable的实例,作为参数传入到了Thread的构造方法中

  • 创建新的类,实现Runnable,但是类的名字是匿名的
  • 创建了这个新类的实例(一次性)
  • 重写run方法

2.5 使用lambda表达式

lambda本质上就是针对匿名内部类的平替

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

相关文章:

JavaEE初阶Day 3:多线程(1)

目录 Day 3&#xff1a;多线程&#xff08;1&#xff09;1. 线程1.1 引入线程的原因1.2 线程的定义1.3 为何线程更轻量1.4 问题 2. 多线程代码2.1 继承Thread重写run2.2 通过实现Runnable接口创建线程2.3 针对2.1的变形使用匿名内部类2.4 针对Runnable创建匿名内部类2.5 使用la…...

gutil140.dll是什么?gutil140.dll无法继续执行的解决方法

gutil140.dll文件是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;通常与Microsoft Visual Studio 2015相关联。 gutil140.dll是开发过程中使用的工具函数集合&#xff0c;它辅助开发人员执行常见的编程任务&#xff0c;如文件操作、内存分配和字符串处理等。这个…...

在CentOS 7上安装Python 3.7.7

文章目录 一、实战步骤1. 安装编译工具2. 下载Python 3.7.7安装包3. 上传Python 3.7.7安装包4. 解压缩安装包5. 切换目录并编译安装6. 配置Python环境变量7. 使配置生效8. 验证安装是否成功 二、实战总结 一、实战步骤 1. 安装编译工具 在终端中执行以下命令 yum -y groupin…...

基于SpringBoot Vue宠物领养系统

一、&#x1f4dd;功能介绍 基于SpringBoot Vue宠物领养系统 角色&#xff1a;管理员、用户 当游客打开系统的网址后&#xff0c;首先看到的就是首页界面。在这里&#xff0c;游客能够看到宠物领养救助平台的导航条显示首页、宠物招领、宠物认领、 宠物论坛、宠物资讯、后台管…...

ip命令

ip a 也是ip addr简写 [rootlocalhost ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft…...

【Kaggle】练习赛《鲍鱼年龄预测》(上)

前言 上一篇文章&#xff0c;讲解了《肥胖风险的多类别预测》机器学习方面的文章&#xff0c;主要是多分类算法的运用&#xff0c;本文是一个回归的算法&#xff0c;本期是2024年4月份的题目《Regression with an Abalone Dataset》即《鲍鱼年龄预测》&#xff0c;在此分享高手…...

Ruby 之交租阶段信息生成

题目 我看了一下&#xff0c;这个题目应该不是什么机密&#xff0c;所以先放上来了。大概意思是根据合同信息生成交租阶段信息。 解答 要求是要使用 Ruby 生成交租阶段信息&#xff0c;由于时间比较仓促&#xff0c;变量名那些就用得随意了些。要点主要有下面这些&#xff1a…...

RUST语言值所有权之内存复制与移动

1.RUST中每个值都有一个所有者,每次只能有一个所有者 String::from函数会为字符串hello分配一块内存 内存示例如下: 在内存分配前调用s1正常输出 在分配s1给s2后调用报错 因为s1分配给s2后,s1的指向自动失效 s1被move到s2 s1自动释放 字符串克隆使用...

【Django学习笔记(三)】BootStrap介绍

BootStrap介绍 前言正文1、BootStrap 快速了解2、初识BootStrap2.1 下载地址2.2 创建目录2.3 引入BootStrap2.4 使用BootStrap 3、BootStrap 组件&样式3.1 导航条3.2 栅格系统3.3 container3.3.1 container3.3.2 container-fluid 3.4 面板3.5 媒体对象3.6 分页3.7 图标3.7.…...

ClickHouse开发相关(UDAF)

ClickHouse开发相关(UDAF) ClickHouse介绍 ClickHouse是一个开源、高性能的列式 OLAP 数据库管理系统,用于使用 SQL 进行实时分析。 为什么需要ClickHouse UDAF? ClickHouse中已存在了许多聚合函数,绝大多数情况下已经覆盖我们的需求,但是有时候我们仍然需要自定义函数…...

MySql并发事务问题

事务 事务概念&#xff1a; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务的特性&#xff1a;ACID&#xff1a; 小…...

Windows下Docker创建Mysql5.7

安装 下载镜像&#xff0c;注意&#xff0c;要带版本号 docker pull mysql:5.7 等下载完成执行命令&#xff1a; 错误命令1&#xff0c;直接Windows下路径&#xff1a; docker run --name mysql57 --restartalways -p 3306:3306 -v F:/mysqldata/data57/log:/var/log/mysql…...

Redis(性能管理、主从复制、哨兵模式)概述及部署

目录 一、性能管理 1、查看Redis内存使用 2、内存碎片率 3、跟踪内存碎片率 4、内存使用率 5、内回收key 二、Redis集群有三种模式 三、Redis主从复制 1、主从复制的概念 2、主从复制的作用 3、主从复制的流程 4、搭建Redis主从复制 1.环境准备 2.安装Redis&#…...

LabVIEW挖坑指南

一、挖坑指南 1.1、输出变量放在条件框内 错误写法&#xff1a; 现象&#xff1a;如果没进入对应的分支&#xff0c;输出为默认值 正常写法&#xff1a; 让每个分支输出的值都在预料之内。 1.2、统计耗时不准 错误写法 现象&#xff1a;统计出来的耗时是2000ms 正常写法&a…...

docker容器环境安装记录(MAC M1)(完善中)

0、背景 在MAC M1中搭建商城项目环境时&#xff0c;采用docker统一管理开发工具&#xff0c;期间碰到了许多环境安装问题&#xff0c;做个总结。 1、安装redis 在宿主机新建redis.conf文件运行创建容器命令&#xff0c;进行容器创建、端口映射、文件挂载、以指定配置文件启动…...

Linux 常用命令(持续更新中...)

1. ls 查看文件列表命令 语法&#xff1a; ls [-a -l -h] [Linux路径] -a -l -h 是可选的选项 &#xff08;-h需配合-l命令一起使用&#xff09;Linux路径是此命令可选的参数 ls #查看当前目录所有非隐藏文件(平铺方式显示) ls -a #查看当前目录下所有文件 …...

xss.pwnfunction-Jefff

在eval中可以直接执行命令所以直接把"直接闭合在结尾再加上一个"因为后面的"没闭和会报错 ?jeffa";alert(1);" 或 ?jeffa"-alert(1)-" -是分隔符...

java——文件上传

一、文件上传——简介 文件上传的简介&#xff1a;文件上传是指将本地计算机中的文件传输到网络上的服务器或另一台计算机上的过程。在 Web 开发中&#xff0c;文件上传通常指的是将用户通过 Web 页面提交的文件&#xff08;如图像、文档、音频、视频等&#xff09;传输到服务器…...

RCE(远程命令执行)漏洞详解

漏洞描述 RCE(remote command/code execute&#xff0c;远程命令执行)漏洞 远程代码执行 (RCE) 攻击是指攻击者可以在一个组织的计算机或网络上运行恶意代码。执行攻击者控制的代码的能力可用于各种目的&#xff0c;包括部署额外的恶意软件或窃取敏感数据。 漏洞原理 远程代…...

K8S - Deployment 的版本回滚

当前状态 先看deployment rootk8s-master:~# kubectl get deploy -o wide --show-labels NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES …...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...

未授权访问事件频发,我们应当如何应对?

在当下&#xff0c;数据已成为企业和组织的核心资产&#xff0c;是推动业务发展、决策制定以及创新的关键驱动力。然而&#xff0c;未授权访问这一隐匿的安全威胁&#xff0c;正如同高悬的达摩克利斯之剑&#xff0c;时刻威胁着数据的安全&#xff0c;一旦触发&#xff0c;便可…...