014_多线程
多线程
- 多线程
- 创建线程
- 方式一:继承Thread类
- 方式二:实现Runable接口
- 方式三:实现Callbale接口
- Thread的常用方法
- 线程安全
- 线程同步
- 方式一:同步代码块
- 同步方法
- 方式三:Lock锁
- 线性池
- 创建线程池
- 处理Runnable任务
- 处理Callable任务
- 通过Executors创建线程池
- 并发/并行
- 并发
- 并行
多线程
- 线程(Thread)是一个程序内部的一条执行流程。
- 程序中如果只有一条执行流程,那这个程序就是单线程的程序。
- 多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。
创建线程
- 启动线程必须是调用start方法,不是调用run方法。
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
- 只有调用start方法才是启动一个新的线程执行。
- 不要把主线程任务放在启动子线程之前。
- 这样主线程一直是先跑完的,相当于是一个单线程的效果了。
方式一:继承Thread类
- 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
- 创建MyThread类的对象
- 调用线程对象的start()方法启动线程(启动后还是执行run方法的)
- 优缺点
- 优点:编码简单
- 缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。
方式二:实现Runable接口
- 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable任务对象
- 把MyRunnable任务对象交给Thread处理。
- 调用线程对象的start()方法启动线程
- 优缺点
- 优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。
- 缺点:需要多一个Runnable对象。
| Thread****类提供的构造器 | 说明 |
|---|---|
| public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
- 匿名内部类写法
- 可以创建Runnable的匿名内部类对象。
- 再交给Thread线程对象。
- 再调用线程对象的start()启动线程。
方式三:实现Callbale接口
- 创建任务对象
- 定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。
- 把Callable类型的对象封装成FutureTask(线程任务对象)。
- 把线程任务对象交给Thread对象。
- 调用Thread对象的start方法启动线程。
- 线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
- 优缺点
- 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。
- 缺点:编码复杂一点。
- FutureTask的API
| FutureTask提供的构造器 | 说明 |
|---|---|
| public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
| FutureTask提供的方法 | 说明 |
|---|---|
| public V get() throws Exception | 获取线程执行call方法返回的结果。 |
Thread的常用方法
| Thread提供的常用方法 | 说明 |
|---|---|
| public void run() | 线程的任务方法 |
| public void start() | 启动线程 |
| public String getName() | 获取当前线程的名称,线程名称默认是Thread-索引 |
| public void setName (String name) | 为线程设置名称 |
| public static Thread currentThread () | 获取当前执行的线程对象 |
| public static void sleep(long time) | 让当前执行的线程休眠多少毫秒后,再继续执行 |
| public final void join()… | 让调用当前这个方法的线程先执行完! |
| Thread 提供的常见构造器 | 说明 |
|---|---|
| public Thread(String name) | 可以为当前线程指定名称 |
| public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
| public Thread(Runnable target, String name) | 封装Runnable对象成为线程对象,并指定线程名称 |
线程安全
- 多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。
- 存在多个线程在同时执行
- 同时访问一个共享资源
- 存在修改该共享资源
线程同步
- 线程同步是线程安全问题的解决方案。
- 线程同步的核心思想
- 让多个线程先后依次访问共享资源,这样就可以避免出现线程安全问题。
- 线程同步的常见方案
- 加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。
方式一:同步代码块
- 作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
- 对出现问题的核心代码使用synchronized进行加锁
- 每次只能一个线程占锁进入访问
- 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。
- 对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。
- 锁对象的使用规范
- 建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
- 对于静态方法建议使用字节码(类名.class)对象作为锁对象。
synchronized(同步锁) {访问共享资源的核心代码
}
同步方法
-
作用:把访问共享资源的核心方法给上锁,以此保证线程安全。
-
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
-
同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
-
如果方法是实例方法:同步方法默认用this作为的锁对象。
-
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
修饰符 synchronized 返回值类型 方法名称(形参列表) {操作共享资源的代码
}
方式三:Lock锁
- Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
- Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
- 锁对象建议使用final修饰,防止被别人篡改
- 释放锁建议将释放锁的操作放到finally代码块中,确保锁用完了一定会被释放
线性池
- 线程池就是一个可以复用线程的技术。
- 不使用线性池的问题
- 用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的, 创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
- 线程池的工作原理
线程池的基本概念是,在应用程序启动时创建一定数量的线程,并将它们保存在线程池中。当需要执行任务时,从线程池中获取一个空闲的线程,将任务分配给该线程执行。当任务执行完毕后,线程将返回到线程池,可以被其他任务复用。- 线程池:{有限的任务队列(WorkQueue)}
- 工作线程WorkThread进行选择任务队列的对象。
- 任务接口
- Runable
- Callable
创建线程池
- JDK 5.0起提供了代表线程池的接口:ExecutorService。
- 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
ExecutorService ——> ThreadPoolExecutor- 通过ThreadPoolExecutor创建线程池。
- 参数一:corePoolSize : 指定线程池的核心线程的数量。
- 参数二:maximumPoolSize:指定线程池的最大线程数量。
- 参数三:keepAliveTime :指定临时线程的存活时间。
- 参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)
- 参数五:workQueue:指定线程池的任务队列。
- 参数六:threadFactory:指定线程池的线程工厂。
- 参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
- 通过ThreadPoolExecutor创建线程池。
| ThreadPoolExecutor类提供的构造器 | 作用 |
|---|---|
| public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) | 使用指定的初始化参数创建一个新的线程池对象 |
- 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。
处理Runnable任务
- ExecutorService的常用方法
| 方法名称 | 说明 |
|---|---|
| void execute(Runnable command) | 执行 Runnable 任务 |
| Future submit(Callable task) | 执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果 |
| void shutdown() | 等全部任务执行完毕后,再关闭线程池! |
| List shutdownNow() | 立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务 |
- 线程池的注意事项
- 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会拒绝新任务? - 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
- 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
- 任务拒绝策略
| 策略 | 说明 |
|---|---|
| ThreadPoolExecutor.AbortPolicy() | 丢弃任务并抛出RejectedExecutionException异常。是默认的策略 |
| ThreadPoolExecutor. DiscardPolicy() | 丢弃任务,但是不抛出异常,这是不推荐的做法 |
| ThreadPoolExecutor. DiscardOldestPolicy() | 抛弃队列中等待最久的任务 然后把当前任务加入队列中 |
| ThreadPoolExecutor. CallerRunsPolicy() | 由主线程负责调用任务的run()方法从而绕过线程池直接执行 |
处理Callable任务
- 线程池使用ExecutorService的方法处理Callable任务,并得到任务执行完后返回的结果。
Future<T> submit(Callable<T> command)
通过Executors创建线程池
- Executors是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
- 方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。
| 方法名称 | 说明 |
|---|---|
| public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
| public static ExecutorService newSingleThreadExecutor() | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
| public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。 |
| public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |
- Executors不是适合做大型互联网场景的线程池方案。
- 建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。
并发/并行
- 正在运行的程序(软件)就是一个独立的进程。
- 线程是属于进程的,一个进程中可以同时运行很多个线程。
- 进程中的多个线程其实是并发和并行执行的。
并发
- 进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并行
- 在同一个时刻上,同时有多个线程在被CPU调度执行。
相关文章:
014_多线程
多线程 多线程创建线程方式一:继承Thread类方式二:实现Runable接口方式三:实现Callbale接口 Thread的常用方法线程安全线程同步方式一:同步代码块同步方法方式三:Lock锁 线性池创建线程池处理Runnable任务处理Callable…...
R语言基础包可视化(一:axis函数)
R语言基础包可视化(一:axis函数) 背景axis函数(坐标轴函数)各参数的图片示例hadj和padjline和poslty,lwd,lwd.ticksgap.axis总结背景 之前在介绍正态Q-Q图的过程中,画过标准正态分布的随机数、分数数、分布函数、密度函数的图像,相关的文章连接参考此处:R语言正态Q-Q图…...
Spring MVC 处理 HTTP 状态码、响应头和异常的完整示例
Spring MVC 处理 HTTP 状态码、响应头和异常的完整示例 1. 正常响应处理 通过 ResponseEntity 可以灵活控制 HTTP 状态码、响应头和响应体。 代码示例:创建资源返回 201 并设置 Location 头 import org.springframework.http.HttpHeaders; import org.springfram…...
Linux安装postgresql17
1、下载 wget https://ftp.postgresql.org/pub/source/v17.4/postgresql-17.4.tar.gz 2、上传解压 tar -zxvf postgresql-17.4.tar.gz 3、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel …...
从零搭建微服务项目Pro(第0章——微服务项目脚手架搭建)
前言: 在本专栏Base第0章曾介绍一种入门级的微服务项目搭建,尽管后续基于此框架上实现了Nacos、Eureka服务注册发现、配置管理、Feign调用、网关模块、OSS文件存储、JSR参数校验、LogBack日志配置,鉴权模块、定时任务模块等,但由于…...
dolphinscheduler创建文件夹显示存储未启用的问题--已解决
只要修改api-server/comf/common.properties和standalone-server/conf/common.properties里面的内容就可以了,应为你要靠standalone-server这个服务启动dolphinscheduler-web,其他就算怎么改你重启dolphinscheduler的时候系统也不会识别新的common.prope…...
C++线段树详解与实现技巧
📚 C++线段树详解与实现技巧 线段树(Segment Tree)是一种高效处理 区间查询 和 区间更新 的数据结构,时间复杂度为 O(log n)。本文结合代码实例,详解其核心原理与实现细节。 🌳 线段树结构特点 完全二叉树:使用数组存储,父子节点关系通过下标计算。区间划分:每个节…...
聊一聊原子操作和弱内存序
1、原子操作概念 在并发编程中,原子操作(Atomic Operation)是实现线程安全的基础机制之一。从宏观上看,原子操作是“不可中断”的单元,但若深入微观层面,其本质是由底层处理器提供的一组特殊指令来保证其原…...
VIRT, RES,SHR之间的关系
VIRT、RES 和 SHR 是进程内存使用的三个关键指标,它们之间的关系反映了进程的内存分配和使用情况。以下是它们的定义和关系: VIRT(虚拟内存):表示进程分配的虚拟内存总量,包括所有代码、数据、共享库、堆栈…...
Ubuntu中部署MeloTTS
0. 环境 ubuntu server 22.04 cuda version 12.4 python version 3.9 1. 安装python依赖 git clone https://github.com/myshell-ai/MeloTTS.git cd MeloTTS注意不是执行 pip install melotts 如果国内服务器无法从github中下载源码,那么可以把github改为gitc…...
2024年React最新高频面试题及核心考点解析,涵盖基础、进阶和新特性,助你高效备战
以下是2024年React最新高频面试题及核心考点解析,涵盖基础、进阶和新特性,助你高效备战: 一、基础篇 React虚拟DOM原理及Diff算法优化策略 • 必考点:虚拟DOM树对比(同级比较、Key的作用、组件类型判断) •…...
Adobe After Effects的插件--------Optical Flares之Options概述
Optical Flares插件的Options是对整个效果的组装和设置。点击该按钮会弹出一个组装室弹窗。 Options组装室就是对每个【镜头对象】进行加工处理,再将其组装在一起,拼凑成完整的光效。 接下来是我对组装室的探索: 面板 面板中有预览、堆栈、编辑和浏览按钮,其作用是调节窗…...
深入解读 React 纯组件(PureComponent)
什么是纯组件? React 的纯组件(PureComponent)是 React.Component 的一个变体,它通过浅比较(shallow comparison)props 和 state 来自动实现 shouldComponentUpdate() 方法,从而优化性能。 核心特点 1. 自动浅比较: PureCompon…...
微信小程序事件详解
微信小程序中的事件绑定是实现交互功能的核心机制之一。通过事件绑定,开发者可以监听用户的操作行为(如点击、输入、滑动等),并根据需要执行相应的逻辑处理。 以下是关于微信小程序事件绑定的详细说明: 一、事件绑定的…...
字符串与相应函数(上)
字符串处理函数分类 求字符串长度:strlen长度不受限制的字符串函数:strcpy,strcat,strcmp长度受限制的字符串函数:strncpy,strncat,strncmp字符串查找:strstr,strtok错误信息报告:strerror字符操作,内存操作函数&…...
Laravel源码进阶
Laravel源码进阶 版本 laravel5.8 生成服务容器 public index.php //compose必要操作 require __DIR__./../vendor/autoload.php; //容器文件 $app require_once __DIR__./../bootstrap/app.php;-bootstrap/app.php //初始化容器 构造函数中执行这个几个方法 //$this->…...
镜舟科技亮相 2025 中国移动云智算大会,展示数据湖仓一体创新方案
4月10-11日,2025 中国移动云智算大会在苏州金鸡湖国际会议中心成功举办。大会以“由云向智,共绘算网新生态”为主题,汇聚了众多行业领袖与技术专家,共同探讨了算力网络与人工智能的深度融合与未来发展趋势。 作为中国领先的企业级…...
2025蓝桥杯省赛C/C++研究生组游记
前言 至少半年没写算法题了,手生了不少,由于python写太多导致行末老是忘记打分号,printf老是忘记写f,for和if的括号也老是忘写,差点连&&和||都忘记了。 题目都是回忆版本,可能有不准确的地方。 …...
重读《人件》Peopleware -(6)Ⅰ管理人力资源Ⅴ-帕金森定律重探 Parkinson’s Law Revisited
1954年,英国作家C. Northcote Parkinson引入了一个概念:工作会膨胀以填满分配给它的时间,这个概念现在被熟知为帕金森定律。如果你不知道很少有管理者接受过任何管理培训的话,你可能会以为他们都参加过一个关于帕金森定律及其影响…...
Linux-内核驱动-led
登记设备号(后面可以动态分配) 自己定义内核函数 登记设备名字和功能 exit和init在内核启动自动执行 这样定义直接操作物理地址 ioctl 定义了设备文件的各种操作,并准备将其注册到内核中。 代码中声明了一个cdev结构体变量cdev,这…...
记录一次因ASM磁盘组空间不足,导致MAP进程无法启动
生产中 ADG 库出现告警,检查发现 map 进程异常: 检查 alter 日志,出现: ORA-19504:failed to create file "DATAC1/casarch/2_162186_1067953047.arc" ORA-17502:ksfdcre:4 Failed to create file ... ORA-15041:diskgroup "DATAC1" space exhausted OR…...
可能存在特殊情况,比如控制台显示有延迟、缓冲问题等影响了显示顺序。
从控制台输出看,正常逻辑应是先执行 System.out.println(" 未处理异常演示 "); 输出对应文本,再因 arr 为 null 访问 length 触发 NullPointerException 输出异常信息。可能存在特殊情况,比如控制台显示有延迟、缓冲问题等影响…...
使用DaemonSet部署集群守护进程集
使用DaemonSet部署集群守护进程集 文章目录 使用DaemonSet部署集群守护进程集[toc]一、使用DaemonSet部署日志收集守护进程集二、管理DaemonSet部署的集群守护进程集1.对DaemonSet执行滚动更新操作2.对DaemonSet执行回滚操作3.删除DaemonSet 一、使用DaemonSet部署日志收集守护…...
c++中继承方面的知识点
继承的概念及定义 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保 持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象 程序设计的层次结…...
PyTorch 学习笔记
环境:python3.8 PyTorch2.4.1cpu PyCharm 参考链接: 快速入门 — PyTorch 教程 2.6.0cu124 文档 PyTorch 文档 — PyTorch 2.4 文档 快速入门 导入库 import torch from torch import nn from torch.utils.data import DataLoader from torchvision …...
Spring AI 结构化输出详解
一、Spring AI 结构化输出的定义与核心概念 Spring AI 提供了一种强大的功能,允许开发者将大型语言模型(LLM)的输出从字符串转换为结构化格式,如 JSON、XML 或 Java 对象。这种结构化输出能力对于依赖可靠解析输出值的下游应用程…...
spring security oauth2.0的四种模式
OAuth 2.0 定义了 4 种授权模式(Grant Type),用于不同场景下的令牌获取。以下是每种模式的详细说明、适用场景和对比: 一、授权码模式(Authorization Code Grant) 适用场景 • Web 应用(有后端…...
从零开始的C++编程 2(类和对象下)
目录 1.构造函数初始化列表 2.类型转换 3.static成员 4.友元 5.内部类 6.匿名对象 1.构造函数初始化列表 ①之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅式,就是初始化列表,初始化…...
Vue 项目中 package.json 文件的深度解析
Vue 项目中 package.json 文件的深度解析 在 Vue 项目中,package.json 文件是项目配置的核心,它管理着项目的依赖关系、脚本命令、版本信息等重要内容。正确理解和配置 package.json 文件,对于项目的开发、构建、测试和部署都至关重要。本文…...
将三维非平面点集拆分为平面面片的MATLAB实现
将三维非平面点集拆分为平面面片的MATLAB实现 要将三维空间中不在同一平面上的点集拆分为多个平面面片,可以采用以下几种方法: 1. 三角剖分法 (Delaunay Triangulation) 最简单的方法是将点集进行三角剖分,因为三个点总是共面的࿱…...
