【JavaEE】_线程与多线程的创建
目录
1. 线程的概念
2. 创建与使用多线程
2.1 方式1:继承Thread类
2.2 方式2: 实现Runnable接口
2.3 以上两种创建线程方式的对比
3. 多线程的优势-增加运行速度
1. 线程的概念
进程的存在是由于系统的多任务执行需求,这也要求程序员进行并发编程;
使用多进程是完全可以实现并发编程的,但如果要频繁地创建或销毁(如分配、销毁内存或文件)以及频繁地调度进程,资源的申请和释放不仅低效,成本也非常高;
为了解决这个问题,通常会通过两个方式:
(1)进程池:效率有一定提高,但进程池中的闲置进程不使用的时候仍然在消耗系统资源,故而使用进程池的系统资源消耗是非常大的;
(2)线程:线程比进程更轻量,每个线程也能够执行一个任务(代码),也能够并发编程;
创建、调度、销毁一个线程的成本相比进程而言要低很多,在Linux上也把线程称为轻量级进程,
进程重量重在资源的申请和释放,线程则是包含在进程中的,一个进程中的多个线程共用同一份资源(同一份内存+文件),只有在创建进程的第一个线程时,由于需要分配资源,成本是相对较高对的,后续在这个进程中再创建其他线程的成本都比较低;
但是并非线程越多越好,如果线程过多,就会存在资源竞争导致速度受限;
注:进程与线程的区别与联系?
(1)进程包含线程,一个进程里可以包含一个线程,也可以包含多个线程;
(2)进程和线程都是为了处理并发编程场景,但进程频繁创建、调度、释放时效率较低,消耗较大;而线程由于少了申请释放资源的过程,故而更轻量,创建、调度、释放都效率更高,消耗更少;
(3)操作系统创建进程需要给进程分配资源,故而进程是操作系统分配资源的基本单位;
操作系统创建线程是要在CPU上调度执行,故而线程是操作系统调度执行的基本单位;
(4)进程具有独立性,每个进程都由各自的虚拟地址空间,进程之间互不影响;
同一个进程中的多个线程共用同一个内存空间,线程之间可能会互相影响;
2. 创建与使用多线程
2.1 方式1:继承Thread类
java标准库提供了一个Thread类来表示、操作线程,Thread类也可视为是java标准库提供的API;
创建好的Thread实例和操作系统中的线程是一一对应的关系;
操作系统提供了一组关于API(C语言),java对于这组API进一步封装形成了Thread类;
示例代码1:单线程创建示例
class MyThread extends Thread{@Overridepublic void run(){System.out.println("Hello Thread.");}
}
public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();}
}
注:(1)通过Thread类创建线程有很多种写法,最简单的就是创建子类继承Thread并且重写run方法;
(2)run方法中描述该线程要执行哪些代码,由于每个线程都是并发执行的,因此需要告知每个线程要执行的代码内容,run方法中的逻辑是在新创建出的线程中被执行的代码;
(3)start方法的调用代表着在系统中真正创建了线程,此时才开始执行上文的run操作;
(4)这里创建线程是在同一个进程中创建的;
(5)线程之间是并发进行的:
(6)线程强制中断异常是多线程中最常遇到的异常之一:
(7)Thread是java.lang中的类,是不需要导入包的,类似的还有String也是不需要导入的;
示例代码2:多线程创建示例
class MyThread extends Thread{@Overridepublic void run(){while(true){System.out.println("Hello Thread");try {Thread.sleep(1000);//休眠:强制使线程进入阻塞状态 单位为ms//即1s内这个线程不会到cpu上执行}catch(InterruptedException e){e.printStackTrace();}}}
}
public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while(true){System.out.println("Hello Main");try {Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}}}
}
截取部分输出结果如下:
注:(1)一个进程中至少会有一个线程,在一个java进程中也至少会有一个调用main方法的线程,只是该线程是系统自动生成的而非手动创建的,此时我们手动创建的t线程与自动创建的main线程就是并发执行的关系,这两个线程从宏观上看该输出结果就是同时执行的;
(2)两个线程都是打印一条语句后休眠1s,当1s结束后,系统先唤醒哪个线程是随机的,即对于操作系统来说,内部对线程之间的调度顺序在宏观上也可以认为是随机的,这种调度方式也称为抢占式执行;
(3)sleep是一个毫秒级休眠语句,并没有那么精确,比如sleep(1000)的含义是1000ms之内不能上CPU,而不是1000ms之后准时上CPU,故而结束阻塞状态的具体时间是不确定的,这与线程之间的调度是随机的也是彼此互相印证的;
(4)start方法用于启动线程;
示例代码3:使用匿名内部类
public class Demo3 {public static void main(String[] args) {//1.创建一个匿名内部类继承自Thread类//2.重写run方法//3.new这个匿名内部类的实例Thread t = new Thread(){public void run(){System.out.println("Hello Thread.");}};t.start();}
}
2.2 方式2: 实现Runnable接口
创建一个类实现Runnable接口,再创建Runnable实例传给Thread实例;
代码示例1:实现Runnable接口创建线程
//Runnable 就是在描述一个任务
class MyRunnable implements Runnable{@Overridepublic void run(){System.out.println("Hello");}
}
public class Demo2 {public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();}
}
通过Runnable来描述任务的内容,进一步地再把描述好的任务交给Thread实例;
代码示例2:使用匿名内部类
public class Demo4 {public static void main(String[] args) {//1.new Runnable的匿名内部类//2.将new的Runnable实例传给Thread的构造方法Thread t = new Thread(new Runnable(){public void run(){System.out.println("Hello Thread.");}});t.start();}
}
需要将new 的Runnable的实例传递给Thread,故而需要包含其重写的run方法;
代码示例3:使用lambda表达式
public class Demo5 {public static void main(String[] args) {Thread t = new Thread(()->{System.out.println("Hello Thread.");});t.start();}
}
lambda表达式就是一个匿名方法,()表示方法参数,->表示是一个lambda,{}中编写方法内容;
2.3 以上两种创建线程方式的对比
通常认为Runnable方式会更好一点,能够做到让线程与线程执行的任务更好地进行解耦;
编写代码通常希望高内聚、低耦合。
Runnable只是描述了一个任务,但是任务的执行方式是进程、线程、线程池还是协程来执行,Runnable内部并不作涉及,Runnable内部的代码也不涉及;
3. 多线程的优势-增加运行速度
多线程编程的优势显著体现在可以提高任务完成的效率:
如现有两个整数变量,分别对这两个变量自增10亿次,分别使用一个线程与两个线程进行演示:
public class Demo6 {private static final long count = 10_0000_0000;public static void serial(){ //串型执行//记录程序执行时间long beg = System.currentTimeMillis();long a = 0;for(long i=0;i<count;i++){a++;}long b = 0;for(long i=0;i<count;i++){b++;}long end = System.currentTimeMillis();System.out.println("Single Thread Time: "+(end-beg)+" ms.");}public static void concurrency() throws InterruptedException {long beg = System.currentTimeMillis();Thread t1 = new Thread(()->{long a = 0;for(int i=0;i<count;i++){a++;}});t1.start();Thread t2 = new Thread(()->{long b = 0;for(int i=0;i<count;i++){b++;}});t2.start();//不能在此处直接记录结束时间,该方法是在main线程中执行的;//main线程与t1、t2线程是并发执行的,即t1、t2尚未执行结束此时就已经记录结束时间了//应将main线程等待t1与t2线程执行完毕再进行计时t1.join();t2.join();//join方法效果就是等待线程结束,哪个线程调用则令main线程等待哪个线程结束long end = System.currentTimeMillis();System.out.println("Multi Thread Time: "+(end-beg)+" ms.");}public static void main(String[] args) throws InterruptedException {serial();concurrency();}
}
输出结果为:
注:(1)增加线程并非一定会达到翻倍的速度提升,因为两个线程在底层到底是并行执行还是并发执行并不确定, 底层微观真正并行执行的时候,效率才会有显著提升;
(2)当count不够大时,反而可能会导致程序执行速度更慢,因为创建线程本身也需要时间开销,此时代码的执行时间反而更多地消耗在了创建线程上;
(3)多线程适合应用于CPU密集型的程序,当程序需要进行大量的计算时,使用多线程就可以更充分地利用CPU的多核资源;
相关文章:

【JavaEE】_线程与多线程的创建
目录 1. 线程的概念 2. 创建与使用多线程 2.1 方式1:继承Thread类 2.2 方式2: 实现Runnable接口 2.3 以上两种创建线程方式的对比 3. 多线程的优势-增加运行速度 1. 线程的概念 进程的存在是由于系统的多任务执行需求,这也要求程序员进…...

【前端工程化面试题】如何优化提高 webpack 的构建速度
使用最新版本的 Webpack 和相关插件: 每个新版本的 Webpack 都会带来性能方面的改进和优化,因此始终确保你在使用最新版本。同时,更新你的相关插件也是同样重要的。 使用DllPlugin动态链接库: 使用DllPlugin和DllReferencePlugin来将第三方库的代码进行…...

免费chatgpt使用
基本功能如下: https://go.aigcplus.cc/auth/register?inviteCode3HCULH2UD...

OpenCV识别人脸案例实战
使用级联函数 基本流程 函数介绍 在OpenCV中,人脸检测使用的是cv2.CascadeClassifier.detectMultiScale()函数,它可以检测出图片中所有的人脸。该函数由分类器对象调用,其语法格式为: objects cv2.CascadeClassifier.detectMul…...
VOSK——离线语音库
文章目录 识别函数调用添加自定义热词表1. SetWords2. SetLatticeWords3. SetPartialWords使用示例注意1. SetMaxAlternatives2. SetNLSML3. SetSpkModel4. SetGrammar使用示例注意SetLogLevel示例用法注意事项 识别函数调用 在使用Vosk库进行语音识别时,PartialRe…...

ELAdmin 隐藏添加编辑按钮
使用场景 做了一个监控模块,数据都是定时生成的,所以不需要手动添加和编辑功能。 顶部不显示 可以使用 true 或者 false 控制现实隐藏 created() {this.crud.optShow {add: false,edit: false,del: true,download: true,reset: true}},如果没有 crea…...
浅谈Websocket
由于 http 存在⼀个明显的弊端(消息只能有客户端推送到服务器端,⽽服务器端不能主动推送到客户端),导致如果服务器如果有连续的变化,这时只能使⽤轮询,⽽轮询效率过低,并不适合。于是 WebSocket 被发明出来 WebSocket 是⼀种在 Web 应⽤程序中实现双向通信的协议。与传…...
JavaScript闭包详细介绍
文章目录 什么是闭包优点:变量持久化:封装私有变量:模块化:函数工厂: 缺点:内存占用:调试困难:过度使用导致性能下降: 什么是闭包 闭包是指有权访问另一个函数作用域中的…...
pytorch神经网络入门代码
import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms# 定义神经网络结构 class SimpleNN(nn.Module):def __init__(self, input_size, hidden_size, num_classes):super(SimpleNN, self).__init_…...

代码随想录算法训练营第三十四天|860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球
860.柠檬水找零 链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 细节: 1. 首先根据题意就是只有5.的成本,然后就开始找钱,找钱也是10.和5. 2. 直接根据10 和 5 进行变量定义,然后去循环…...

Ditto:提升剪贴板体验的宝藏软件(复制粘贴效率翻倍、文本处理好助手)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、什么是Ditto?二、下载安装三、如…...
【自然语言处理-工具篇】spaCy<2>--模型的使用
前言 之前已经介绍了spaCy的安装,接下来我们要通过下载和加载模型去开始使用spaCy。 下载模型 经过训练的 spaCy 管道可以作为 Python 包安装。这意味着它们是应用程序的一个组件,就像任何其他模块一样。可以使用 spaCy download的命令安装模型,也可以通过将 pip 指向路径或…...
Java之通过Jsch库连接Linux实现文件传输
Java之通过JSch库连接Linux实现文件传输 文章目录 Java之通过JSch库连接Linux实现文件传输1. JSch2. Java通过Jsch连接Linux1. poxm.xml2. 工具类3. 调用案例 1. JSch 官网:JSch - Java Secure Channel (jcraft.com) JSch是SSH2的纯Java实现。 JSch 允许您连接到 ss…...
Nginx七层负载均衡之动静分离
思路: servera:负载均衡服务器 serverb:静态服务器 serverc:动态服务器 serverd:默认服务器 servera(192.168.233.132): # 安装 Nginx 服务器 yum install nginx -y#关闭防火墙和selinux systemctl stop firewalld setenforce 0# 切换到 Nginx 配置文…...
305_C++_定义了一个定时器池 TimerPool 类和相关的枚举类型和结构体
头文件:定义了一个定时器池 TimerPool 类和相关的枚举类型和结构体 #ifndef TIMERPOOL_H #define TIMERPOOL_H #include "rsglobal.h" #include "taskqueue.h" #incl...

大整数因数分解工具——yafu
一、安装 yafu--下载链接 二、配置环境变量,直接从cmd打开 1.找到yafu-x64.exe 所在的文件路径 2.点击设置——系统——系统信息——高级系统设置——环境变量——点击PATH(上下都可以)——新建 添加yafu-x64.exe 所在路径——点击确定 3…...
非关系型数据库(NOSQL)和关系型数据库(SQL)区别详解
前言: 在我们的日常开发中,关系型数据库和非关系型数据库的使用已经是一个成熟的软件产品开发过程中必不可却的存储数据的工具了。那么用了这么久的关系数据库和非关系型数据库你们都知道他们之间的区别了吗?下面我们来详细的介绍一下。 关系…...
7.Cloud-GateWay
0.概述 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 1.入门配置 1.1 POM <!--新增gateway--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-sta…...

【Linux】Framebuffer 应用
# 前置知识 LCD 操作原理 在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。 Frame 是帧的意思, buffer 是缓冲的意思,这意味着 Framebuffer 就是一块内存,里面保存着一帧图像。 Framebuffer 中保存着一帧图像的每一个像素颜色值&…...
markdown绘制流程图相关代码片段记录
有时候会使用typora来绘制一些流程图,进行编码之类的工作,在网络搜集了一些笔记,做个记录,方便日后进行复习,相关的记录如下: 每次作图时,代码以「graph <布局方向>」开头,如…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...