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

Java多线程下载文件

JVM是支持多线程程序的,当程序需要同时执行两个或多个任务,实现一些需要等待的任务时,如用户输入、文件读写、网络操作、搜索等多线程程序比单线程程序更具优势,可充分利用CPU资源,完成时间更短,提高应用程序的响应,增强用户体验。因此学会改善程序结构,将即长又复杂的进程分为多个线程,独立去运行,对于开发者来说至关重要。

以下载多个文件为例,如何使用多线程机制,高效率的完成下载任务?且听我我慢慢道来。

提出需求:编写一个API,打包下载GitHub的所有用户头像(以zip形式返回所有用户头像)。

在这里插入图片描述

文件压缩我们统一使用apache的commons-compress相关类进行压缩,因此需要引入相关的依赖

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.24.0</version>
</dependency>

完整代码:

/*** TODO** @Description* @Author laizhenghua* @Date 2023/8/31 09:22**/
@RestController
@SpringBootApplication
public class TestApplication {public static void main(String[] args) {SpringApplication.run(TestApplication.class, args);}@Autowiredprivate ServletContext servletContext;@GetMapping("/test")public void test() {ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletResponse response = servletRequestAttributes.getResponse();RestTemplate restTemplate = new RestTemplate();String usersUrl = "https://api.github.com/users";// 查询github用户信息JSONArray userList = restTemplate.getForObject(usersUrl, JSONArray.class);if (CollectionUtils.isEmpty(userList)) {fallback("下载失败,失败原因: 查询为空", response);return;}// 下载路径准备String rootPath = servletContext.getRealPath("/") + "avatars";File root = new File(rootPath);if (!root.exists()) {root.mkdir();}// 初始化线程池(JDK 5.0新增的线程池API更多知识可自行学习)ExecutorService executorService = Executors.newFixedThreadPool(10);userList.forEach(item -> {JSONObject user = new JSONObject((Map)item);String login = user.getString("login"); // github登录名String downloadUrl = user.getString("avatar_url"); // 头像下载地址String filePath = rootPath + File.separator + login + ".png";// 执行下载任务(下载至本地)// ****** 一个线程处理一个用户(主线程只负责提交任务尽可能把耗时逻辑都放到多线程任务里如下载、IO操作等) ******executorService.execute(() -> {try {File file = new File(filePath);boolean newFile = file.createNewFile();if (!newFile) {return;}String name = Thread.currentThread().getName();String log = String.format("[%s] download start --- download path: %s", name, filePath);System.out.println(log);// 调用下载接口获取输入流程ResponseEntity<Resource> responseEntity = restTemplate.getForEntity(downloadUrl, Resource.class);// 将得到的输入流写入文件InputStream inputStream = null;OutputStream outputStream = null;try {inputStream = Objects.requireNonNull(responseEntity.getBody()).getInputStream();outputStream = new FileOutputStream(file);byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}} catch (IOException e) {e.printStackTrace();} finally {IOUtils.close(inputStream);IOUtils.close(outputStream);}} catch (IOException e) {e.printStackTrace();}});});// 关闭线程池executorService.shutdown();// 使用org.apache.commons类压缩下载好的头像ZipArchiveOutputStream zipAos = null;try {// 等待线程池中所有任务执行完成(指定时间内没有执行完则返回false)boolean allTaskCompleted = executorService.awaitTermination(30, TimeUnit.MINUTES);if (!allTaskCompleted) {fallback("下载失败", response);}// 设置下载信息response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode("github_avatar.zip", "utf-8") + "\"");response.setContentType("application/zip");zipAos = new ZipArchiveOutputStream(response.getOutputStream());zipAos.setEncoding("UTF-8");zipAos.setUseZip64(Zip64Mode.AsNeeded);File[] files = root.listFiles(); // 获取所有下载好的头像assert files != null;for (File file : files) {// 将头像压缩至 github_avatar.zip 文件ZipArchiveEntry entry = new ZipArchiveEntry(file, file.getName());entry.setLastModifiedTime(FileTime.fromMillis(file.lastModified()));zipAos.putArchiveEntry(entry);try (InputStream inputStream = new FileInputStream(file)) {byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) != -1) {zipAos.write(buffer, 0, len);}file.delete(); // 删除文件}}zipAos.closeArchiveEntry();} catch (Exception e) {e.printStackTrace();} finally {IOUtils.close(zipAos);}}private void fallback(String message, HttpServletResponse response) {response.setCharacterEncoding("UTF-8");response.setContentType(MediaType.APPLICATION_JSON_VALUE);PrintWriter writer = null;try {R error = R.error(500, message);JSONObject json = new JSONObject(error);writer = response.getWriter();writer.append(json.toString());} catch (IOException e) {e.printStackTrace();} finally {if (writer != null) {writer.close();}}}
}

对于打包下载,我们可以用单线程,也可以用多线程,处理这种任务多线程的优势就体现出来了,可自行对比下单线程和多线程程序响应速度。

在这里插入图片描述

使用多线程需要注意的是:

  1. Executors.newFixedThreadPool()是创建一个可重用固定线程数量的线程池。
  2. 主线程只负责分配任务,把耗时的逻辑尽可能的写到多线程任务上独立执行。
  3. 使用完线程池必须要关闭,先调用 shutdown() 方法关闭线程池,然后调用 awaitTermination(long timeout, TimeUnit unit) 方法等待线程池中的所有任务执行完成,只有线程池中的所有任务都执行完了,才能把响应信息写到response上。

相关文章:

Java多线程下载文件

JVM是支持多线程程序的&#xff0c;当程序需要同时执行两个或多个任务&#xff0c;实现一些需要等待的任务时&#xff0c;如用户输入、文件读写、网络操作、搜索等多线程程序比单线程程序更具优势&#xff0c;可充分利用CPU资源&#xff0c;完成时间更短&#xff0c;提高应用程…...

oracle 同一张表同时insert多条数据 mysql 同一张表同时insert多条数据

oracle 同一张表同时insert多条数据 在Oracle数据库中&#xff0c;你可以使用INSERT ALL语句同时向同一张表插入多条数据。INSERT ALL语句允许你一次执行多个插入操作&#xff0c;可以提高插入的效率和速度。 以下是使用INSERT ALL语句插入多条数据的示例&#xff1a; INSERT…...

ROS键盘遥控机器人,通过参数服务器指定速度

1、引言 在上节的驱动机器人&#xff0c;我们知道是cmd_vel话题发布一串Twist类型消息来控制&#xff0c;我们可以输入如下命令查看这个Twist的详细信息&#xff1a;rosmsg show geometry_msgs/Twist geometry_msgs/Vector3 linear float64 x float64 y float64 z geome…...

具有快表的地址变换机构

1.快表&#xff08;TLB&#xff09; 快表&#xff0c;又称联想寄存器(TLB&#xff0c;translation lookaside buffer)&#xff0c; 是一种访问速度比内存快很多的高速缓存(TLB不是内存! )&#xff0c; 用来存放最近访问的页表项的副本&#xff0c;可以加速地址变换的速度。 与…...

【使用python和flask建个人博客】修复侧边栏最新文章、最多阅读等链接不能打开的问题

自从上次因版本兼容问题修改过部分代码之后,好长时间没光顾woniunote这个个人博客模块了,最近发文章的时候发现侧边栏的文章打不开,定位了bug,并进行了修复。 <div class="col-12 side"><div class="tip" align...

ShareX使用说明——优秀的录屏软件

ShareX初识 ShareX 是一个自由及开放源代码的截图录像软件&#xff0c;目前仅支持Windows系统。 项目源代码在GitHub平台上发布&#xff0c; 软件可以在Microsoft商店和Steam上下载。 ShareX is a free and open source program that lets you capture or record any area of y…...

10.14~10.15verilog操作流程与Block Design

后面的那个是延时精度 verilog文件结构 文件名称与写的模板没有关系&#xff0c;这个文件名为P1,但模板名为andgate 但是如果是仿真文件&#xff0c;就需要开头的模板名和仿真文件名相同 .v是源文件&#xff0c;设计文件 .v在设计与sim里都有&#xff0c;静态共享&#xff0…...

小解C语言文件编译过程【linux】

小解C语言文件编译过程【linux】 库动态库静态库 C语言文件 程序编译过程整体预处理编译汇编链接动态链接静态链接两种方法对比 库 看到标题是文件编译过程 但是开头却是库&#xff0c;这可不是挂羊头卖狗肉&#xff0c;而是因为库也是代码不可缺少的一部分&#xff0c;并且在…...

[Python]黑色背景白色块滑动视频

黑色背景白色块滑动视频&#xff0c;单帧效果如下&#xff1a; 配置参数 1920 1080 400 400 300 60 1920x1080.avi import numpy as np import cv2 as cv import os import syswidth 1920 height 1080 rect_szx 400 rect_szy 300 sz_y_init 400 fps 24width int(sys.a…...

【linux kernel】对linux内核设备的注册机制和查找机制分析

文章目录 1、简介2、device_initialize分析3、device_add分析4、总结 &#x1f53a;【linux内核系列文章】 &#x1f449;对一些文章内容进行了勘误&#xff0c;本系列文章长期不定时更新&#xff0c;希望能分享出优质的文章&#xff01; 1、《linux内核数据结构分析之哈希表》…...

asp.net酒店餐饮管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net酒店餐饮管理系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 ASP.NE 酒店餐饮管理系统 二、功能…...

38_Nginx 启动流程

文章目录 src/core/nginx.cint ngx_cdecl main(int argc, char *const *argv) {ngx_buf_t *b;...

数据特征选择 | Lasso特征选择(Python)

文章目录 效果一览文章概述源码设计小结效果一览 文章概述 Lasso算法是一种经典的线性回归算法,被广泛应用于特征选择和降维问题。相较于传统的线性回归算法,Lasso算法能够在保持预测准确性的同时,自动筛选出对目标变量影响较大的特征变量,从而达到降低模型复杂度、提高泛化…...

最小覆盖子串[困难]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个字符串s、一个字符串t。返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有字符的子串&#xff0c;则返回空字符串"" 。 对于t中重复字符&#xff0c;我们寻找的子字符串中该字符数量必须不少于t中该字符数量…...

保姆级搭建Mysql 并进行视图可视化操作

安装MySQL数据库 选择mysql5.7.36_x32.msi”&#xff0c;双击运行&#xff0c;如下图所示&#xff1a; 在此窗口中&#xff0c;选择“Custom”选项&#xff0c;点击“Next>”进入下一步&#xff1b; 在此窗口中&#xff0c;选择号下的MySQL Server 5.7.36 – x64&…...

设计模式的学习顺序

设计模式的学习顺序可以按照以下步骤进行&#xff1a; 掌握基础知识&#xff1a;先确保你对编程语言和软件开发的基本概念有深入的理解&#xff0c;包括面向对象编程、继承、多态等。学习常用设计模式&#xff1a;首先学习并理解一些常用的设计模式&#xff0c;例如单例模式、…...

数据结构和算法——树结构

二叉树 又叫二叉排序树。 节点是数量为&#xff0c;&#xff0c;n为层数。 满二叉树&#xff1a;所有的叶子节点都在最后一层。 完全二叉树&#xff1a;如果所有叶子节点都在最后一层和倒数第二层&#xff0c;而且每个叶子节点都有左右子节点。 完全二叉树 前序遍历 1、先输…...

【Java】Integer包装类

Integer&#xff1a;对基本数据类型 int 实现包装 方法名称说明public Integer&#xff08;int value&#xff09;根据 int 值创建 Integer 对象&#xff08;JDK9以后过时&#xff09;public integer&#xff08;String s&#xff09;根据 String 值创建 Integer 对象&#xf…...

Web后端开发登录校验及JWT令牌,过滤器,拦截器详解

如果用户名正确则成功进入 登录功能 代码 Controller Service Mapper 结果 若登录成功结果如下: 如果登录失败,结果如下 登录校验 为什么需要登录校验 有时再未登录情况下, 我们也可以直接访问部门管理, 员工管理等功能 因此我们需要一个登录校验操作, 只有确认用户登录…...

大语言模型迎来重大突破!找到解释神经网络行为方法

前不久&#xff0c;获得亚马逊40亿美元投资的ChatGPT主要竞争对手Anthropic在官网公布了一篇名为《朝向单义性&#xff1a;通过词典学习分解语言模型》的论文&#xff0c;公布了解释经网络行为的方法。 由于神经网络是基于海量数据训练而成&#xff0c;其开发的AI模型可以生成…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...