视频门户网站建设方案/万网官网域名查询
目录
- 一、前情提要
- 二、JDK8的CompletableFuture
- 1、ForkJoinPool
- 2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别
- 三、通过CompletableFuture优化 “通过Future获取异步返回值”
- 1、通过Future获取异步返回值关键代码
- (1)将异步方法的返回值改为```Future<Integer>```,将返回值放到```new AsyncResult<>();```中;
- (2)通过```Future<Integer>.get()```获取返回值:
- 2、通过CompletableFuture获取异步返回值关键代码
- (1)将异步方法的返回值改为 int
- (2)通过```completableFuture.get()```获取返回值
- 3、效率对比
- (1)测试环境
- (2)统计四种情况下10万数据入库时间
- (3)设置核心线程数
- 自定义ForkJoinPool线程池
- 自定义线程池
- (4)统计分析
- 四、通过CompletableFuture.allOf解决阻塞主线程问题
- 1、语法
- 2、代码实例
- 五、CompletableFuture中花俏的语法糖
- 1、runAsync
- 2、supplyAsync
- 六、顺序执行异步任务
- 1、thenRun
- 2、thenAccept
- 3、thenApply
- 七、CompletableFuture合并任务
- 八、CompletableFuture VS Future总结
- 在BUG中磨砺,在优化中成长
大家好,我是哪吒。
一、前情提要
在上一篇文章中,使用双异步后,如何保证数据一致性?,通过Future获取异步返回值,轮询判断Future状态,如果执行完毕或已取消,则通过get()获取返回值,get()是阻塞的方法,因此会阻塞当前线程,如果通过new Runnable()执行get()方法,那么还是需要返回AsyncResult,然后再通过主线程去get()获取异步线程返回结果。
写法很繁琐,还会阻塞主线程。
下面是FutureTask异步执行流程图:
二、JDK8的CompletableFuture
1、ForkJoinPool
Java8中引入了CompletableFuture,它实现了对Future的全面升级,可以通过回调的方式,获取异步线程返回值。
CompletableFuture的异步执行通过ForkJoinPool实现, 它使用守护线程去执行任务。
ForkJoinPool在于可以充分利用多核CPU的优势,把一个任务拆分成多个小任务,把多个小任务放到多个CPU上并行执行,当多个小任务执行完毕后,再将其执行结果合并起来。
Future的异步执行是通过ThreadPoolExecutor实现的。
2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别
- ForkJoinPool中的每个线程都会有一个队列,而ThreadPoolExecutor只有一个队列,并根据queue类型不同,细分出各种线程池;
- ForkJoinPool在使用过程中,会创建大量的子任务,会进行大量的gc,但是ThreadPoolExecutor不需要,因为ThreadPoolExecutor是任务分配平均的;
- ThreadPoolExecutor中每个异步线程之间是相互独立的,当执行速度快的线程执行完毕后,它就会一直处于空闲的状态,等待其它线程执行完毕;
- ForkJoinPool中每个异步线程之间并不是绝对独立的,在ForkJoinPool线程池中会维护一个队列来存放需要执行的任务,当线程自身任务执行完毕后,它会从其它线程中获取未执行的任务并帮助它执行,直至所有线程执行完毕。
因此,在多线程任务分配不均时,ForkJoinPool的执行效率更高。但是,如果任务分配均匀,ThreadPoolExecutor的执行效率更高,因为ForkJoinPool会创建大量子任务,并对其进行大量的GC,比较耗时。
三、通过CompletableFuture优化 “通过Future获取异步返回值”
1、通过Future获取异步返回值关键代码
(1)将异步方法的返回值改为Future<Integer>
,将返回值放到new AsyncResult<>();
中;
@Async("async-executor")
public void readXls(String filePath, String filename) {try {// 此代码为简化关键性代码List<Future<Integer>> futureList = new ArrayList<>();for (int time = 0; time < times; time++) {Future<Integer> sumFuture = readExcelDataAsyncFutureService.readXlsCacheAsync();futureList.add(sumFuture);}}catch (Exception e){logger.error("readXlsCacheAsync---插入数据异常:",e);}
}
@Async("async-executor")
public Future<Integer> readXlsCacheAsync() {try {// 此代码为简化关键性代码return new AsyncResult<>(sum);}catch (Exception e){return new AsyncResult<>(0);}
}
(2)通过Future<Integer>.get()
获取返回值:
public static boolean getFutureResult(List<Future<Integer>> futureList, int excelRow) {int[] futureSumArr = new int[futureList.size()];for (int i = 0;i<futureList.size();i++) {try {Future<Integer> future = futureList.get(i);while (true) {if (future.isDone() && !future.isCancelled()) {Integer futureSum = future.get();logger.info("获取Future返回值成功"+"----Future:" + future+ ",Result:" + futureSum);futureSumArr[i] += futureSum;break;} else {logger.info("Future正在执行---获取Future返回值中---等待3秒");Thread.sleep(3000);}}} catch (Exception e) {logger.error("获取Future返回值异常: ", e);}}boolean insertFlag = getInsertSum(futureSumArr, excelRow);logger.info("获取所有异步线程Future的返回值成功,Excel插入结果="+insertFlag);return insertFlag;
}
2、通过CompletableFuture获取异步返回值关键代码
(1)将异步方法的返回值改为 int
@Async("async-executor")
public void readXls(String filePath, String filename) {List<CompletableFuture<Integer>> completableFutureList = new ArrayList<>();for (int time = 0; time < times; time++) {// 此代码为简化关键性代码CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {@Overridepublic Integer get() {return readExcelDbJdk8Service.readXlsCacheAsyncMybatis();}}).thenApply((result) -> {// 回调方法return thenApplyTest2(result);// supplyAsync返回值 * 1}).thenApply((result) -> {return thenApplyTest5(result);// thenApply返回值 * 1}).exceptionally((e) -> { // 如果执行异常:logger.error("CompletableFuture.supplyAsync----异常:", e);return null;});completableFutureList.add(completableFuture);}
}
@Async("async-executor")
public int readXlsCacheAsync() {try {// 此代码为简化关键性代码return sum;}catch (Exception e){return -1;}
}
(2)通过completableFuture.get()
获取返回值
public static boolean getCompletableFutureResult(List<CompletableFuture<Integer>> list, int excelRow){logger.info("通过completableFuture.get()获取每个异步线程的插入结果----开始");int sum = 0;for (int i = 0; i < list.size(); i++) {Integer result = list.get(i).get();sum += result;}boolean insertFlag = excelRow == sum;logger.info("全部执行完毕,excelRow={},入库={}, 数据是否一致={}",excelRow,sum,insertFlag);return insertFlag;
}
3、效率对比
(1)测试环境
- 12个逻辑处理器的电脑;
- Excel中包含10万条数据;
- Future的自定义线程池,核心线程数为24;
- ForkJoinPool的核心线程数为24;
(2)统计四种情况下10万数据入库时间
- 不获取异步返回值
- 通过Future获取异步返回值
- 通过CompletableFuture获取异步返回值,默认ForkJoinPool线程池的核心线程数为本机逻辑处理器数量,测试电脑为12;
- 通过CompletableFuture获取异步返回值,修改ForkJoinPool线程池的核心线程数为24。
备注:因为CompletableFuture不阻塞主线程,主线程执行时间只有2秒,表格中统计的是异步线程全部执行完成的时间。
(3)设置核心线程数
将核心线程数CorePoolSize设置成CPU的处理器数量,是不是效率最高的?
// 获取CPU的处理器数量
int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;// 测试电脑是24
因为在接口被调用后,开启异步线程,执行入库任务,因为测试机最多同时开启24线程处理任务,故将10万条数据拆分成等量的24份,也就是10万/24 = 4166,那么我设置成4200,是不是效率最佳呢?
测试的过程中发现,好像真的是这样的。
自定义ForkJoinPool线程池
@Autowired
@Qualifier("asyncTaskExecutor")
private Executor asyncTaskExecutor;@Override
public void readXls(String filePath, String filename) {List<CompletableFuture<Integer>> completableFutureList = new ArrayList<>();for (int time = 0; time < times; time++) {CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {@Overridepublic Integer get() {try {return readExcelDbJdk8Service.readXlsCacheAsync(sheet, row, start, finalEnd, insertBuilder);} catch (Exception e) {logger.error("CompletableFuture----readXlsCacheAsync---异常:", e);return -1;}};},asyncTaskExecutor);completableFutureList.add(completableFuture);}// 不会阻塞主线程CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()])).whenComplete((r,e) -> {try {int insertSum = getCompletableFutureResult(completableFutureList, excelRow);} catch (Exception ex) {return;}});
}
自定义线程池
/*** 自定义异步线程池*/
@Bean("asyncTaskExecutor")
public AsyncTaskExecutor asyncTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//设置线程名称executor.setThreadNamePrefix("asyncTask-Executor");//设置最大线程数executor.setMaxPoolSize(200);//设置核心线程数executor.setCorePoolSize(24);//设置线程空闲时间,默认60executor.setKeepAliveSeconds(200);//设置队列容量executor.setQueueCapacity(50);/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;
}
(4)统计分析
效率对比:
③通过CompletableFuture获取异步返回值(12线程) < ②通过Future获取异步返回值 < ④通过CompletableFuture获取异步返回值(24线程) < ①不获取异步返回值
不获取异步返回值时性能最优,这不废话嘛~
核心线程数相同的情况下,CompletableFuture的入库效率要优于Future的入库效率,10万条数据大概要快4秒钟,这还是相当惊人的,优化的价值就在于此。
四、通过CompletableFuture.allOf解决阻塞主线程问题
1、语法
CompletableFuture.allOf(CompletableFuture的可变数组).whenComplete((r,e) -> {})
。
2、代码实例
getCompletableFutureResult方法在 “3.2.2 通过completableFuture.get()
获取返回值”。
// 不会阻塞主线程
CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()])).whenComplete((r,e) -> {logger.info("全部执行完毕,解决主线程阻塞问题~");try {int insertSum = getCompletableFutureResult(completableFutureList, excelRow);} catch (Exception ex) {logger.error("全部执行完毕,解决主线程阻塞问题,异常:", ex);return;}
});// 会阻塞主线程
//getCompletableFutureResult(completableFutureList, excelRow);logger.info("CompletableFuture----会阻塞主线程吗?");
五、CompletableFuture中花俏的语法糖
1、runAsync
runAsync 方法不支持返回值。
可以通过runAsync执行没有返回值的异步方法。
不会阻塞主线程。
// 分批异步读取Excel内容并入库
int finalEnd = end;
CompletableFuture.runAsync(() -> readExcelDbJdk8Service.readXlsCacheAsyncMybatis();
2、supplyAsync
supplyAsync也可以异步处理任务,传入的对象实现了Supplier接口。将Supplier作为参数并返回CompletableFuture结果值,这意味着它不接受任何输入参数,而是将result作为输出返回。
会阻塞主线程。
supplyAsync()方法关键代码:
int finalEnd = end;
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {@Overridepublic Integer get() {return readExcelDbJdk8Service.readXlsCacheAsyncMybatis();}
});
@Override
public int readXlsCacheAsyncMybatis() {// 不为人知的操作// 返回异步方法执行结果即可return 100;
}
六、顺序执行异步任务
1、thenRun
thenRun()不接受参数,也没有返回值,与runAsync()配套使用,恰到好处。
// JDK8的CompletableFuture
CompletableFuture.runAsync(() -> readExcelDbJdk8Service.readXlsCacheAsyncMybatis())
.thenRun(() -> logger.info("CompletableFuture----.thenRun()方法测试"));
2、thenAccept
thenAccept()接受参数,没有返回值。
supplyAsync + thenAccept
- 异步线程顺序执行
- supplyAsync的异步返回值,可以作为thenAccept的参数使用
- 不会阻塞主线程
CompletableFuture.supplyAsync(new Supplier<Integer>() {@Overridepublic Integer get() {return readExcelDbJdk8Service.readXlsCacheAsyncMybatis();}
}).thenAccept(x -> logger.info(".thenAccept()方法测试:" + x));
但是,此时无法通过completableFuture.get()获取supplyAsync的返回值了。
3、thenApply
thenApply在thenAccept的基础上,可以再次通过completableFuture.get()获取返回值。
supplyAsync + thenApply,典型的链式编程。
- 异步线程内方法顺序执行
- supplyAsync 的返回值,作为第 1 个thenApply的参数,进行业务处理
- 第 1 个thenApply的返回值,作为第 2 个thenApply的参数,进行业务处理
- 最后,通过future.get()方法获取最终的返回值
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {@Overridepublic Integer get() {return readExcelDbJdk8Service.readXlsCacheAsyncMybatis();}
}).thenApply((result) -> {return thenApplyTest2(result);// supplyAsync返回值 * 2
}).thenApply((result) -> {return thenApplyTest5(result);// thenApply返回值 * 5
});logger.info("readXlsCacheAsyncMybatis插入数据 * 2 * 5 = " + completableFuture.get());
七、CompletableFuture合并任务
- thenCombine,多个异步任务并行处理,有返回值,最后合并结果返回新的CompletableFuture对象;
- thenAcceptBoth,多个异步任务并行处理,无返回值;
- acceptEither,多个异步任务并行处理,无返回值;
- applyToEither,,多个异步任务并行处理,有返回值;
CompletableFuture合并任务的代码实例,这里就不多赘述了,一些语法糖而已,大家切记陷入低水平勤奋的怪圈。
八、CompletableFuture VS Future总结
本文中以下几个方面对比了CompletableFuture和Future的差异:
- ForkJoinPool和ThreadPoolExecutor的实现原理,探索了CompletableFuture和Future的差异;
- 通过代码实例的形式简单介绍了CompletableFuture中花俏的语法糖;
- 通过CompletableFuture优化了 “通过Future获取异步返回值”;
- 通过CompletableFuture.allOf解决阻塞主线程问题。
Future提供了异步执行的能力,但Future.get()会通过轮询的方式获取异步返回值,get()方法还会阻塞主线程。
轮询的方式非常消耗CPU资源,阻塞的方式显然与我们的异步初衷背道而驰。
JDK8提供的CompletableFuture实现了Future接口,添加了很多Future不具备的功能,比如链式编程、异常处理回调函数、获取异步结果不阻塞不轮询、合并异步任务等。
获取异步线程结果后,我们可以通过添加事务的方式,实现Excel入库操作的数据一致性。
异步多线程情况下如何实现事务?
有的小伙伴可能会说:
这还不简单?添加@Transactional注解,如果发生异常或入库数据量不符,直接回滚就可以了~
那么,真的是这样吗?我们下期见~
在BUG中磨砺,在优化中成长
使用双异步后,从 191s 优化到 2s
增加索引 + 异步 + 不落地后,从 12h 优化到 15 min
使用懒加载 + 零拷贝后,程序的秒开率提升至99.99%
性能优化2.0,新增缓存后,程序的秒开率不升反降
🏆文章收录于:100天精通Java从入门到就业
全网最细Java零基础手把手入门教程,系列课程包括:Java基础、Java8新特性、Java集合、高并发、性能优化等,适合零基础和进阶提升的同学。
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。
华为OD机试 2023B卷题库疯狂收录中,刷题点这里
刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。
相关文章:

获取双异步返回值时,如何保证主线程不阻塞?
目录 一、前情提要二、JDK8的CompletableFuture1、ForkJoinPool2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别 三、通过CompletableFuture优化 “通过Future获取异步返回值”1、通过Future获取异步返回值关键代码(1)将异步…...

hosts文件修改后无法保存的解决办法
目录 第一步 右键点击C盘里的hosts文件,选择重命名。 第二步 在桌面新建一个txt文件,命名为hosts。并把自己需要的内容写入保存。 第三步 把hosts.txt文件复制到原本hosts文件的路径下。右键选中hosts.txt文件,选择重命名,去掉…...

源码篇--Redis 五种数据类型
文章目录 前言一、 字符串类型:1.1 字符串的编码格式:1.1.1 raw 编码格式:1.1.2 empstr编码格式:1.1.3 int 编码格式:1.1.4 字符串存储结构展示: 二、 list类型:2.1 List 底层数据支持:2.2 List 源码实现:2.3 List 结构…...

Sulfo Cy2 Biotin,水溶性 Cy2 生物素,能够与各种氨基基团特异性结合
您好,欢迎来到新研之家 文章关键词:Sulfo Cyanine2 Biotin,Sulfo Cy2 Biotin,水溶性 Cy2 生物素,Sulfo-Cy2-Biotin,水溶性-Cy2-生物素 一、基本信息 产品简介:Sulfo Cyanine2 Biotin, also k…...

NineData支持制定安全、可靠的SQL开发规范
在和数据库打交道中,不管是数据库管理员(DBA)还是开发人员,经常会做一些CURD操作。因为每个人对数据库的了解程度不一样,所以在项目上线时,往往还需要专职人员对数据库的CURD操作进行审核,确保C…...

LSTM时间序列预测
本文借鉴了数学建模清风老师的课件与思路,可以点击查看链接查看清风老师视频讲解:【1】演示:基于LSTM深度学习网络预测时间序列(MATLAB工具箱)_哔哩哔哩_bilibili % Forecast of time series based on LSTM deep learn…...

Rocky8 顺利安装 Airflow 并解决数据库报错问题
rocky是替代centos的服务器系统,稳定可靠。rocky8会比centos7新,可以支持更多服务软件的安装,免去升级各种库的麻烦,本文运行airflow服务就用rocky8系统。airflow是一个定时任务管理系统,功能强大,目前是ap…...

[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR)
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR) 1. 数学推导2. 案例反洗与代码详解 1. 数学推导 2. 案例反洗与代码详解...

Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs
大开眼界?探索多模态模型种视觉编码器的缺陷。 论文中指出,上面这些VQA问题,人类可以瞬间给出正确的答案,但是多模态给出的结果却是错误的。是哪个环节出了问题呢?视觉编码器的问题?大语言模型出现了幻觉&…...

汤姆·齐格弗里德《纳什均衡与博弈论》笔记(4)博弈论与人性
第五章 弗洛伊德的梦——博弈和大脑 大脑和经济学 曾经有一段时间——就像在弗洛伊德的年代——心理学家们无法准确地回答人类行为背后的大脑机制。但随着现代神经科学的兴起,情形改变了。比如,人类的情绪不再像过去一样是个谜。科学家们可以观察当人们…...

MacOS平台翻译OCR软件,双管齐下,还可自定义插件,为其添砖加瓦!
小编昨天为大家分享了Windows系统下的一款功能强大且免费的 OCR 开源工具 Umi-OCR。 今天则为大家推荐一款 MacOS系统下的一款 翻译 OCR 多功能双管齐下的桌面应用软件 Bob。这款软件虽然也上线了GitHub,但它不是一款开源软件,仓库只是作者为了用户反馈…...

使用docker配置semantic slam
一.Docker环境配置 1.拉取Docker镜像 sudo docker pull ubuntu:16.04拉取的为ununtu16版本镜像,环境十分干净,可以通过以下命令查看容器列表 sudo docker images 如果想删除多余的docker image,可以使用指令 sudo docker rmi -f <id&g…...

面试常问的Spring AOP底层原理
AOP底层原理可以划分成四个阶段:创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段 第一阶段:创建代理对象阶段 通过getBean()方法创建Bean实例根据AOP的配置匹配目标类的类名,判断是否满足切…...

C++拾遗(四)引用与指针
引用和指针是两种不同的概念,尽管它们在某些方面有一些相似之处,但它们在功能和用途上是有所区别 声明与定义 引用:引用是别名,是对已存在变量的另一个称呼,一旦一个变量被引用,就不能再被引用其他变 量…...

k8s架构、工作流程、集群组件详解
目录 k8s概述 特性 作用(为什么使用) k8s架构 k8s工作流程 k8s集群架构与组件 核心组件详解 Master节点 Kube-apiserver Kube-controller-manager Kube-scheduler 存储中心 etcd Node Kubelet Kube-Proxy 网络通信模型 容器引擎 k8s核…...

CF1362C Johnny and Another Rating Drop(二进制、复杂度考虑)
看完数据范围 n ∈ [ 1 , 1 e 18 ] n\in[1,1e18] n∈[1,1e18]就可以先猜一下要不是可以直接推公式,不能暴力去做,更不能遍历一遍,又看到这种2进制的题目,要猜是不是 l o g log log级别的复杂度。 可以依次考虑每一位 所有 i % 2 …...

1 - 搭建Redis数据库服务器|LNP+Redis
搭建Redis数据库服务器|LNPRedis 搭建Redis数据库服务器相关概念Redis介绍安装RedisRedis服务常用管理命令命令set 、 mset 、 get 、 mget命令keys 、 type 、 exists 、 del命令ttl 、 expire 、 move 、 flushdb 、flushall 、save、shutdown 配置文件解析 LNP …...

米贸搜|Facebook“精准营销”越来越难?或许是“受众定位”没彻底搞清!
一、为何要确定目标受众 对于每个广告主而言,面向最有可能成为其客户的用户营销非常重要,因此,确定目标受众,是Facebook广告投放中极其重要的一环。 二、什么是目标受众? 目标受众是您希望向其传达营销信息…...

【C++】内存分区模型
目录 1.程序运行前 2.程序运行后 3. new操作符 3.1 基本语法 3.2 开辟数组 C程序在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统进行管理的 全局区: 存放全局变量和静态变量以及常量栈区 …...

通过strings二进制文件分析工具排查 version ‘GLIBC_2.25‘ not found 报错
strings命令简介 strings命令用于打印文件中可打印字符串, 可以打印文本文件、可执行程序,库文件等。一般用于分析可执行程序和库文件。strings命令较为常用的功能有以下两种 查看系统的GLIBC版本和目标文件的依赖的GLIBC版本,当系统的GLIBC版本与目标文…...

基于 MQTT 的开源桥接器:自由控制物联网设备 | 开源日报 No.151
Koenkk/zigbee2mqtt Stars: 10.5k License: GPL-3.0 Zigbee2MQTT 是一个 Zigbee 🐝 到 MQTT 桥接器 🌉,可以摆脱专有的 Zigbee 桥接器 🔨 允许您在不使用供应商桥接器或网关的情况下使用 Zigbee 设备通过 MQTT 桥接事件并控制 Z…...

【QT+QGIS跨平台编译】之七:【libjpeg+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、libjpeg介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libjpeg介绍 libjpeg是一个广泛使用的jpeg图像压缩和解压的函数库,采用 C 语言开发。 2013年1月,Independent JPEG Group发布了版本9,对新引入的无损编码模式进行了改进。2022年1月,发布了版…...

VI / VIM的使用
vi/vim 的区别简单点来说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且 还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于 x win…...

第十二站(20天):C++泛型编程
模板 C提供了模板(template)编程的概念。所谓模板,实际上是建立一个通用函数或类, 其 类内部的类型和函数的形参类型不具体指定 ,用一个虚拟的类型来代表。这种通用的方式称 为模板。 模板是泛型编程的基础, 泛型编程即以一种独立于任何特定…...

【Docker】Dokcer学习① - 简介
【Docker】Docker学习① - 简介 一、Docker简介1. Docker是什么2. Docker组成3. Docker对比虚拟机4. Linux Namespace技术5. Linux control groups6. 容器管理工具 二、Docker安装及基础命令介绍三、Docker镜像管理四、Docker镜像与制作五、Docker数据管理六、网络部分七、Dock…...

PostgreSQL 100条命令
我会为您提供一些 PostgreSQL 中最常用的命令: 1. 创建数据库:CREATE DATABASE database_name; 2. 连接到数据库:\c database_name; 3. 创建表格:CREATE TABLE table_name (column1 datatype, column2 datatype, ...); 4. 插入数…...

HTTP动态代理的原理及其对网络性能的影响
HTTP动态代理是一种通过代理服务器来转发HTTP请求和响应数据的网络技术,它可以优化网络性能、提高网络安全性,并解决跨域请求的问题。本文将详细介绍HTTP动态代理的原理及其对网络性能的影响。 一、HTTP动态代理的原理 HTTP动态代理的基本原理是在客户…...

69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey
文章目录 一:简介二:Go标准库compress/gzip包介绍ConstantsVariablestype Headertype Reader 三:代码实践1、压缩与解压工具包2、单元测试3、为何压缩后还要用base64编码 代码地址: https://gitee.com/lymgoforIT/golang-trick/t…...

JavaScript实现的一些小案例
小案例 灯开关案例 <body><img id"light" src"img/off.jpg"><script>var light document.getElementById("light");var flag false;if(flag){light.src "img/on.jpg";flag false;}else{light.src "img/…...

MVC模式
Model-View-Controller : 模型-视图-控制器模式,用于应用程序的分层开发。 Model(模型):代表一个存取数据的对象。也可以带有逻辑,在数据变化时更新控制器。 View(视图):代表模型包含的数据的可视化。 Controller(控制器)…...