高并发编程JUC之进程与线程高并发编程JUC之进程与线程
1.准备
pom.xml 依赖如下:
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version><scope>provided</scope></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.22</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>compile</scope></dependency></dependencies>
logback.xml 配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%date{HH:mm:ss} [%t] %logger - %m%n</pattern></encoder></appender><logger name="c" level="debug" additivity="false"><appender-ref ref="STDOUT"/></logger><root level="ERROR"><appender-ref ref="STDOUT"/></root>
</configuration>
2.进程与线程
2.1 进程与线程
进程
- 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在 指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的 。
- 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
- 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器 等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)
线程
- 一个进程之内可以分为一到多个线程。
- 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行
- Java 中,线程作为最小调度单位,进程作为资源分配的最小单位。 在 windows 中进程是不活动的,只是作 为线程的容器
二者对比
- 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
- 进程拥有共享的资源,如内存空间等,供其内部的线程共享
- 进程间通信较为复杂
-
- 同一台计算机的进程通信称为 IPC(Inter-process communication)
-
- 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
- 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
- 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低
2.2 并行与并发
单核cpu下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows 下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是同时运行的 。总结为一句话就是: 微观串行,宏观并行 。
一般会将这种线程轮流使用 CPU 的做法称为并发, concurrent
| CPU | 时间片 1 | 时间片 2 | 时间片 3 | 时间片 4 |
|---|---|---|---|---|
| core | 线程 1 | 线程 2 | 线程 3 | 线程 4 |
多核 cpu下,每个 核(core) 都可以调度运行线程,这时候线程可以是并行的。
| CPU | 时间片 1 | 时间片 2 | 时间片 3 | 时间片 4 |
|---|---|---|---|---|
| core1 | 线程 1 | 线程 2 | 线程 3 | 线程 4 |
| core2 | 线程 4 | 线程 4 | 线程 2 | 线程 2 |
引用 Rob Pike 的一段描述:
并发(concurrent)是同一时间应对(dealing with)多件事情的能力 。
并行(parallel)是同一时间动手做(doing)多件事情的能力。
2.3 应用
$\textcolor{Green}{*应用之异步调用(案例1)} $
需要等待结果
这时既可以使用同步处理,也可以使用异步来处理
join 实现(同步)
static int result = 0;
private static void test1() throws InterruptedException {log.debug("开始");Thread t1 = new Thread(() -> {log.debug("开始");sleep(1);log.debug("结束");result = 10;}, "t1");t1.start();t1.join();log.debug("结果为:{}", result);
}
输出
20:30:40.453 [main] c.TestJoin - 开始
20:30:40.541 [Thread-0] c.TestJoin - 开始
20:30:41.543 [Thread-0] c.TestJoin - 结束
20:30:41.551 [main] c.TestJoin - 结果为:10
评价
- 需要外部共享变量,不符合面向对象封装的思想
- 必须等待线程结束,不能配合线程池使用
Future 实现(同步)
private static void test2() throws InterruptedException, ExecutionException {log.debug("开始");FutureTask<Integer> result = new FutureTask<>(() -> {log.debug("开始");sleep(1);log.debug("结束");return 10;});new Thread(result, "t1").start();log.debug("结果为:{}", result.get());
}
输出
10:11:57.880 c.TestSync [main] - 开始
10:11:57.942 c.TestSync [t1] - 开始
10:11:58.943 c.TestSync [t1] - 结束
10:11:58.943 c.TestSync [main] - 结果为:10
评价
- 规避了使用 join 之前的缺点
- 可以方便配合线程池使用
private static void test3() throws InterruptedException, ExecutionException {ExecutorService service = Executors.newFixedThreadPool(1);log.debug("开始");Future<Integer> result = service.submit(() -> {log.debug("开始");sleep(1);log.debug("结束");return 10;});log.debug("结果为:{}, result 的类型:{}", result.get(), result.getClass());service.shutdown();
}
输出
10:17:40.090 c.TestSync [main] - 开始
10:17:40.150 c.TestSync [pool-1-thread-1] - 开始
10:17:41.151 c.TestSync [pool-1-thread-1] - 结束
10:17:41.151 c.TestSync [main] - 结果为:10, result 的类型:class java.util.concurrent.FutureTask
评价
- 仍然是 main 线程接收结果
- get 方法是让调用线程同步等待
自定义实现(同步)
见模式篇:保护性暂停模式
CompletableFuture 实现(异步)
private static void test4() {// 进行计算的线程池ExecutorService computeService = Executors.newFixedThreadPool(1);// 接收结果的线程池ExecutorService resultService = Executors.newFixedThreadPool(1);log.debug("开始");CompletableFuture.supplyAsync(() -> {log.debug("开始");sleep(1);log.debug("结束");return 10;}, computeService).thenAcceptAsync((result) -> {log.debug("结果为:{}", result);}, resultService);
}
输出
10:36:28.114 c.TestSync [main] - 开始
10:36:28.164 c.TestSync [pool-1-thread-1] - 开始
10:36:29.165 c.TestSync [pool-1-thread-1] - 结束
10:36:29.165 c.TestSync [pool-2-thread-1] - 结果为:10
评价
- 可以让调用线程异步处理结果,实际是其他线程去同步等待
- 可以方便地分离不同职责的线程池
- 以任务为中心,而不是以线程为中心
BlockingQueue 实现(异步)
private static void test6() {ExecutorService consumer = Executors.newFixedThreadPool(1);ExecutorService producer = Executors.newFixedThreadPool(1);BlockingQueue<Integer> queue = new SynchronousQueue<>();log.debug("开始");producer.submit(() -> {log.debug("开始");sleep(1);log.debug("结束");try {queue.put(10);} catch (InterruptedException e) {e.printStackTrace();}});consumer.submit(() -> {try {Integer result = queue.take();log.debug("结果为:{}", result);} catch (InterruptedException e) {e.printStackTrace();}});
}
不需等待结果
这时最好是使用异步来处理
普通线程实现
@Slf4j(topic = "c.FileReader")
public class FileReader {public static void read(String filename) {int idx = filename.lastIndexOf(File.separator);String shortName = filename.substring(idx + 1);try (FileInputStream in = new FileInputStream(filename)) {long start = System.currentTimeMillis();log.debug("read [{}] start ...", shortName);byte[] buf = new byte[1024];int n = -1;do {n = in.read(buf);} while (n != -1);long end = System.currentTimeMillis();log.debug("read [{}] end ... cost: {} ms", shortName, end - start);} catch (IOException e) {e.printStackTrace();}}
}
没有用线程时,方法的调用是同步的:
@Slf4j(topic = "c.Sync")
public class Sync {public static void main(String[] args) {String fullPath = "E:\1.mp4";FileReader.read(fullPath);log.debug("do other things ...");}
}
输出
18:39:15 [main] c.FileReader - read [1.mp4] start ...
18:39:19 [main] c.FileReader - read [1.mp4] end ... cost: 4090 ms
18:39:19 [main] c.Sync - do other things ...
使用了线程后,方法的调用时异步的:
private static void test1() {new Thread(() -> FileReader.read(Constants.MP4_FULL_PATH)).start();log.debug("do other things ...");
}
输出
18:41:53 [main] c.Async - do other things ...
18:41:53 [Thread-0] c.FileReader - read [1.mp4] start ...
18:41:57 [Thread-0] c.FileReader - read [1.mp4] end ... cost: 4197 ms
线程池实现
private static void test2() {ExecutorService service = Executors.newFixedThreadPool(1);service.execute(() -> FileReader.read(Constants.MP4_FULL_PATH));log.debug("do other things ...");service.shutdown();
}
输出
11:03:31.245 c.TestAsyc [main] - do other things ...
11:03:31.245 c.FileReader [pool-1-thread-1] - read [1.mp4] start ...
11:03:33.479 c.FileReader [pool-1-thread-1] - read [1.mp4] end ... cost: 2235 ms
CompletableFuture 实现
private static void test3() throws IOException {CompletableFuture.runAsync(() -> FileReader.read(Constants.MP4_FULL_PATH));log.debug("do other things ...");System.in.read();
}
输出
11:09:38.145 c.TestAsyc [main] - do other things ...
11:09:38.145 c.FileReader [ForkJoinPool.commonPool-worker-1] - read [1.mp4] start ...
11:09:40.514 c.FileReader [ForkJoinPool.commonPool-worker-1] - read [1.mp4] end ... cost: 2369 ms
以调用方角度来讲,
- 如果 需要等待结果返回,才能继续运行就是同步
- 不需要等待结果返回,就能继续运行就是异步
1.设计
多线程可以让方法执行变为异步的(即不要巴巴干等着)、比如说读取磁盘文件时,假设读取操作花费了 5 秒钟,如果没有线程调度机制,这 5 秒 cpu 什么都做不了,其它代码都得暂停…
2.结论
- 比如在项目中,视频文件需要转换格式等操作比较费时,这时开一个新线程处理视频转换,避免阻塞主线程
- tomcat 的异步 servlet 也是类似的目的,让用户线程处理耗时较长的操作,避免阻塞 tomcat 的工作线程
- ui 程序中,开线程进行其他操作,避免阻塞 ui 线程
充分利用多核 cpu 的优势,提高运行效率。想象下面的场景,执行 3 个计算,最后将计算结果汇总。
计算 1 花费 10 ms
计算 2 花费 11 ms
计算 3 花费 9 ms
汇总需要 1 ms
- 如果是串行执行,那么总共花费的时间是 10 + 11 + 9 + 1 = 31ms
- 但如果是四核 cpu,各个核心分别使用线程 1 执行计算 1,线程 2 执行计算 2,线程 3 执行计算 3,那么 3 个 线程是并行的,花费时间只取决于最长的那个线程运行的时间,即 11ms 最后加上汇总时间只会花费 12ms
注意:
需要在多核 cpu 才能提高效率,单核仍然时是轮流执行
1.设计
代码见【应用之效率-案例1】
2.结论
- 单核 cpu 下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换,不同线程轮流使用 cpu ,不至于一个线程总占用 cpu,别的线程没法干活
- 多核 cpu 可以并行跑多个线程,但能否提高程序运行效率还是要分情况的
-
- 有些任务,经过精心设计,将任务拆分,并行执行,当然可以提高程序的运行效率。但不是所有计算任 务都能拆分(参考后文的【阿姆达尔定律】)
-
- 也不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义
- IO 操作不占用 cpu,只是我们一般拷贝文件使用的是【阻塞 IO】,这时相当于线程虽然不用 cpu,但需要一 直等待 IO 结束,没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化。
相关文章:
高并发编程JUC之进程与线程高并发编程JUC之进程与线程
1.准备 pom.xml 依赖如下: <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target&g…...
css基础
1-css引入方式内嵌式style(学习)<style>p {height: 200;}</style>外联式link(实际开发)<link rel"stylesheet" href"./2-my.css">2-选择器2.1标签选择器(标签名相同的都生效&am…...
Unity - 搬砖日志 - BRP 管线下的自定义阴影尺寸(脱离ProjectSettings/Quality/ShadowResolution设置)
文章目录环境原因解决CSharp 脚本效果预览 - Light.shadowCustomResolution效果预览 - Using Quality Settings应用ControlLightShadowResolution.cs ComponentTools Batching add the Component to all LightReferences环境 Unity : 2020.3.37f1 Pipeline : BRP 原因 (好久没…...
如何在SSMS中生成和保存估计或实际执行计划
在引擎数据库执行查询时执行的过程的步骤由称为查询计划的一组指令描述。查询计划在SQL Server中也称为SQL Server执行计划,我们可以通过以下步骤来生成和保存估计或实际执行计划。 估计执行计划和实际执行计划是两种执行计划: 实际执行计划:当执行查询时,实际执行计划出…...
mac 环境下安装MongoDB
目录 一、下载MongoDB数据库并进行安装 二. 解压放在/usr/local目录下 三. 配置环境变量 “无法验证开发者”的解决方法 mongodb可视化工具的安装与使用 一、下载MongoDB数据库并进行安装 下载地址:https://www.mongodb.com/try/download/community 二. 解压…...
RTOS中相对延时和绝对延时的区别
相信许多朋友都有过这么一个需求:固定一个时间(周期)去处理某一件事情。 比如:固定间隔10ms去采集传感器的数据,然后通过一种算法计算出一个结果,最后通过指令发送出去。 你会通过什么方式解决呢…...
Solon2 项目整合 Nacos 配置中心
网上关于 Nacos 的使用介绍已经很多了,尤其是与 SpringBoot 的整合使用。怎么安装也跳过了,主要就讲 Nacos 在 Solon 里的使用,这个网上几乎是没有的。 1、认识 Solon Solon 一个高效的应用开发框架:更快、更小、更简单…...
Linux 路由表说明
写在前面: 本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。 目录route 命令字段分…...
MIPI协议
MIPI调试指南Rev.0.1 June 18, 2019 © 2018 Horizon Robotics. All rights reserved.Revision HistoryThissection tracks the significant documentation changes that occur fromrelease-to-release. The following table lists the technical content changes foreach …...
第十届CCF大数据与计算智能大赛总决赛暨颁奖典礼在苏州吴江顺利举办
2月24日-25日,中国计算机学会(CCF)主办、苏州市吴江区人民政府支持,苏州市吴江区工信局、吴江区东太湖度假区管理办公室、苏州市吴江区科技局、CCF大数据专家委员会、CCF自然语言处理专业委员会、CCF高性能计算专业委员会、CCF计算…...
PMP高分上岸人士的备考心得,分享考试中你还不知道的小秘密
上岸其实也不是什么特别难的事情,考试一共就180道选择题,题目只要答对60.57%就可以通过考试,高分通过没在怕的,加油备考呀朋友们! 这里也提一嘴,大家备考的时候比较顾虑的一个问题就是考试究竟要不要报班…...
ubuntu下编译libpq和libpqxx库
ubuntu下编译libpq和libpqxx库,用于链接人大金仓 上篇文章验证了libpqxx可以链接人大金仓数据库,这篇文章尝试自己编译libpq和libpqxx库。 文章目录ubuntu下编译libpq和libpqxx库,用于链接人大金仓libpq下载libpq库看看有没有libpq库编译lib…...
ESP-C2系列模组开发板简介
C2是一个芯片采用4毫米x 4毫米封装,与272 kB内存。它运行框架,例如ESP-Jumpstart和ESP造雨者,同时它也运行ESP-IDF。ESP-IDF是Espressif面向嵌入式物联网设备的开源实时操作系统,受到了全球用户的信赖。它由支持Espressif以及所有…...
linux权限管理
权限管理 文件的权限针对三类对象进行定义: owner属主,缩写ugroup属组,缩写gother其他,缩写o 1、文件的一般权限 (1)r,w,x的作用及含义: 权限对文件影响对目录影响r(read…...
提高生活质量,增加学生对校园服务的需求,你知道有哪些?
随着电子商务平台利用移动互联网的趋势提高服务质量,越来越多的传统企业开始关注年轻大学生消费者的校园市场。 提高生活质量,增加学生对校园服务的需求 大学生越来越沉迷于用手机解决生活中的“吃、喝、玩、乐”等服务,如“吃、喝”——可…...
Antlr4:使用grun命令,触发NoClassDefFoundError
1. 意外的发现 在学习使用grun命令时,从未遇到过错误 最近使用grun命令,却遇到了NoClassDefFoundError的错误,使得grun测试工具无法成功启动 错误复现: 使用antlr4命令编译Hello.g4文件,并为指定package(…...
基于rootfs构建Docker镜像
1. 背景 在实际工作中,由于系统本身版本过低,在接受新项目时出现系统版本过低而无法开始工作的问题。 为了解决该问题,使用Docker构建基于ubuntu-18.04的Docker镜像,以解决版本兼容问题。 2. 构建rootfs 2.1. 下载ubuntu-18.0…...
电脑文件软件搬家迁移十大工具
10 大适用于 Windows 的数据迁移软件。 数据迁移至关重要,几乎所有组织都依赖于此。如果您认为数据传输不是一件容易的事,那么数据迁移软件可以帮上忙。 1、奇客电脑迁移 将现有操作系统、软件、文件迁移到 新电脑的最佳方法之一是使用名为奇客电脑迁移…...
【数据库】排名问题
返回第N高的一个解决思路返回N组中的第N高解决思路分数排名解决思路窗口函数数据库经常被用来解决排名问题。 返回第N高的一个 单表查询: 表: Employee------------------- | Column Name | Type | ------------------- | id | int | | salary | int | ----…...
【redis学习篇】主从哨兵集群架构详解
一、Redis主从架构 1.1 redis主从架构搭建 1、复制一份redis.conf文件 2、将相关配置修改为如下值: port 6380 pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件 logfile "6380.log" dir /usr/local/redis-5.0.3/data/6380 # 指…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
