- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
最近接到一个需求,批量上传图片到服务器及实时更新上传进度。当处理大量文件上传任务时,效率是一个关键因素。传统的串行方式会导致任务耗时较长,而使用并发处理可以极大地提高上传效率。想到很久之前用 CompletableFuture 优化过一些多统计的业务场景,效果都还不错,因此在这里也使用它来优化一下上传的效率.
。
CompletableFuture 类是Java 8引入的,它实现了 Future 和 CompletionStage 接口,提供了更强大和灵活的异步编程功能。 CompletableFuture 除了具有 Future 的特性外,还提供了更多的操作和组合方式来处理异步任务。它可以更方便地处理异步任务,实现并发编程,并提供更好的异常处理和结果转换机制。在进行异步编程时, CompletableFuture 是一个更为强大和推荐的选择.
主要特点:
异步执行:允许将任务提交给后台线程,在任务执行期间不会阻塞主线程。这样可以提高应用程序的响应性能,特别是在处理I/O密集型操作时,如网络请求或数据库查询.
链式调用和组合操作:支持链式调用,可以将多个异步任务按照顺序连接起来形成一个任务流水线。每个任务的执行依赖于前一个任务的结果,这种串行的处理方式可以简化异步任务的编写和管理.
异常处理:提供了异常处理的机制,可以通过异常回调方法来捕获和处理任务执行过程中的异常情况。这样可以更好地控制和处理任务执行过程中的异常,提供更健壮的代码.
转换和合并结果:提供了一系列的转换和合并操作,可以对任务的结果进行映射、转换和合并。这样可以方便地对任务的结果进行处理和转换,得到最终期望的结果.
多任务并行执行:支持等待多个任务并行执行,并等待它们全部完成或任意一个完成。这种能力使得在处理并发任务时可以更好地利用系统资源,提高任务执行的效率.
。
测试批量上传了1000张图片,每张图片在579KB,一共564MB。使用串行方式上传,总时长为501秒,使用并行方式上传,总时长是108秒,通过对比优化前后的代码,可以明显看出使用 CompletableFuture 并发处理方式的效率更高。由于任务是并行执行的,多核处理器的能力得到了充分的利用,从而大大提高了批量上传的速度.
。
串行处理方式 。
/** * describe: 批量上传图片 * * @param files 图片文件集合 * @param fileId 文件夹id * @param scheduleKey 上传进度key * @date 2023年06月28日 11:42:03 * @author Tang */ @Override public BatchUploadVO batchUpload2(MultipartFile[] files, Long fileId, String scheduleKey) { //取上传配置 String jsonStr = CacheConfigure.getValue(CacheKeyConstant.IMG_RESOURCE_UPLOAD_CONFIG, String.class); ImgResourceUploadConfigDTO config = JSONObject.toJavaObject(JSONObject.parseObject(jsonStr), ImgResourceUploadConfigDTO.class); List<String> imgTypeList = Arrays.asList(config.getImgType().split(",")); List<String> errorNames = Lists.newCopyOnWriteArrayList(); String userName = SecurityAuthorHolder.getSecurityUser().getUsername(); for(MultipartFile file : files){ try { RedisUtil.setInteger(CacheKeyConstant.UPLOAD_SCHEDULE_TOTAL + scheduleKey, files.length, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf(".") + 1); ServerException.Assert(!imgTypeList.contains(suffix), "文件格式不正确,支持" + String.join(",", imgTypeList)); ServerException.Assert(file.getSize() > config.getMaxSize() * 1024, "文件最大不能超过" + config.getMaxSize() + "K"); //上传 ImgResourceEntity saveData = upload(file, config); saveData.setFileId(fileId); saveData.setCreator(userName); baseMapper.insert(saveData); //缓存自增 供轮询查询实时进度 RedisUtil.incrementValue(CacheKeyConstant.UPLOAD_SCHEDULE_SUCCESS + scheduleKey, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); } catch (Exception e) { errorNames.add(file.getOriginalFilename()); RedisUtil.incrementValue(CacheKeyConstant.UPLOAD_SCHEDULE_ERROR + scheduleKey, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); } } BatchUploadVO vo = schedule(scheduleKey); vo.setErrFileNames(errorNames); return vo; }
串行处理调用时间 。
。
并行处理方式 。
/** * describe: 批量上传图片 * * @param files 图片文件集合 * @param fileId 文件夹id * @param scheduleKey 上传进度key * @date 2023年06月28日 11:42:03 * @author Tang */ @Override public BatchUploadVO batchUpload(MultipartFile[] files, Long fileId, String scheduleKey) { ExecutorService executor = Executors.newFixedThreadPool(10); //取上传配置 String jsonStr = CacheConfigure.getValue(CacheKeyConstant.IMG_RESOURCE_UPLOAD_CONFIG, String.class); ImgResourceUploadConfigDTO config = JSONObject.toJavaObject(JSONObject.parseObject(jsonStr), ImgResourceUploadConfigDTO.class); List<String> imgTypeList = Arrays.asList(config.getImgType().split(",")); List<String> errorNames = Lists.newCopyOnWriteArrayList(); String userName = SecurityAuthorHolder.getSecurityUser().getUsername(); CompletableFuture<Void> allFutures = CompletableFuture.allOf( Arrays.stream(files).map(v -> CompletableFuture.runAsync( () -> { try { RedisUtil.setInteger(CacheKeyConstant.UPLOAD_SCHEDULE_TOTAL + scheduleKey, files.length, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); String suffix = Objects.requireNonNull(v.getOriginalFilename()).substring(v.getOriginalFilename().lastIndexOf(".") + 1); ServerException.Assert(!imgTypeList.contains(suffix), "文件格式不正确,支持" + String.join(",", imgTypeList)); ServerException.Assert(v.getSize() > config.getMaxSize() * 1024, "文件最大不能超过" + config.getMaxSize() + "K"); //上传 ImgResourceEntity saveData = upload(v, config); saveData.setFileId(fileId); saveData.setCreator(userName); baseMapper.insert(saveData); //缓存自增 供轮询查询实时进度 RedisUtil.incrementValue(CacheKeyConstant.UPLOAD_SCHEDULE_SUCCESS + scheduleKey, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); } catch (Exception e) { errorNames.add(v.getOriginalFilename()); RedisUtil.incrementValue(CacheKeyConstant.UPLOAD_SCHEDULE_ERROR + scheduleKey, CacheTimeConstant.BATCH_UPLOAD_EXPIRED_TIME); } }, executor) ).toArray(CompletableFuture[]::new) ); // 等待所有 CompletableFuture 完成 allFutures.join(); // 关闭线程池 executor.shutdown(); BatchUploadVO vo = schedule(scheduleKey); vo.setErrImgFileNames(errorNames); return vo; }
并行调用处理时间 。
。
线程池的使用:为了实现并发处理,可以使用线程池来管理并执行异步任务。通过合理设置线程池的大小和参数,可以控制并发线程的数量和资源的利用率.
异常处理:在并发处理中,每个任务都是独立执行的,因此需要适当处理任务中可能出现的异常情况,避免异常的影响扩散.
进度更新:为了实时更新上传进度,可以将每个任务的进度信息保存到Redis中,并在前端通过轮询查询的方式获取最新的进度信息.
线程安全:确保上传逻辑的线程安全性,避免多线程环境下的竞态条件和数据一致性问题.
。
使用 CompletableFuture 来优化批量上传任务是一种高效且灵活的方式。通过并发处理,我们可以充分利用多核处理器的能力,提高任务的执行效率。同时,通过实时更新上传进度并返回总体的上传结果,可以给用户更好的体验。 在实现过程中,我们需要合理使用线程池、处理异常、保证数据同步和线程安全,以确保上传任务的稳定性和性能。同时,我们还可以利用 CompletableFuture 提供的方法来处理任务的结果、异常和其他相关操作,以满足具体的业务需求。 通过使用 CompletableFuture 进行批量上传任务的优化,可以显著提高系统的性能和用户体验,适用于需要处理大量并发任务的场景.
。
最后此篇关于CompletableFuture之批量上传的文章就讲到这里了,如果你想了解更多关于CompletableFuture之批量上传的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这是我在阅读了几个关于 jpa 批量插入的主题后创建的简单示例,我有 2 个持久对象用户和站点。一个用户可以有多个站点,所以我们在这里有一对多的关系。假设我想创建用户并将多个站点创建/链接到用户帐户。
我有文档列表(对象),该对象有多个文档,即存在 Json 记录,但是当我尝试上传文档束(记录)时,它没有上传到文档数据库,但当我上传单个文档记录时,它上传成功。 List listObj = ne
我希望进行批量域名查找,看看是否有一些域名可供购买。我找不到 perl 模块,但似乎应该有一种方法可以在 perl 中执行此操作。我正在寻找免费的东西。谢谢! 最佳答案 从这里:http://www.
我制作了一个批处理类来检查 FTP 上的文件、下载它们并在 FTP 上删除它们。 当我手动运行它(不是批量运行)时,它运行完美,下载 FTP 中的所有文件并在下载完成后删除它们。 当我尝试批量运行时,
我有一个 *+* 形式的字符串 base。我想得到+之前的所有内容。例如,如果 base=foo+bar,我想获取 foo。 我尝试过使用字符串替换来实现 set left=%base:+*=% 但这
我需要创建几十个表,并且我需要它们是innodb, 有没有办法做到这一点,而不是将 engine=innodb 附加到每个 create table 语句? 最佳答案 可以在服务器级别指定默认引擎,在
我正在尝试制作显示 unix/linux 提示符的 dos shell。代码是: @echo off :hi set tmpdrv=%cd:~0,2% if %homedrive% == %tmpdr
我有以下代码,基本上是在二维矩阵的每一行上进行一维卷积。卷积核是一样的。所以真的是 SIMD 案例。 a = [ 1,2,3,4,5; 6,7,8,9,7; 7,6
情况: 我尝试在 shell 中的循环内移动文件,但我的代码无法正常工作。 for /D %%F in (*) do ( if "%%F" NEQ "%directoryToPutFilesIn
目录包含 2 个(或更多)任意名称的视频文件。 video1.mkv video2.mkv 需要找出每个视频的持续时间。为此,我们使用 MediaInfo . setlocal EnableDelay
如何在 Windows 中批量删除数千个文件中的空格(而不是替换为下划线)?我可以从 DOS 命令执行此操作吗? 目前: file one.mp3 file two.mp3 所有文件需要变成: fil
我想创建一个批处理文件,它读取 2 个不同的值,并根据它们的比较方式进行相应处理。但是,比较永远不会起作用。代码是: REM string1 and string2 contain the follo
我正在尝试将一个文件夹的子文件夹复制到许多其他名称未知的文件夹中。目的是在所有使用它的员工文件夹中备份程序的源文件。如果在员工文件夹中找不到程序文件夹,则不应执行任何操作。这看起来如下: 来源: F:
我正在寻找一种简单的方法来检测一小段文本(几句话)是否为英语。在我看来,这个问题比尝试检测任意语言要容易得多。有没有可以做到这一点的软件?我正在用 python 编写,并且更喜欢 python 库,但
我们正在尝试向 8k 种不同的设备发送促销推送消息。我们正在成功响应推送通知 URL https://fcm.googleapis.com/fcm/send 但只有部分用户收到此通知,并非全部。那么
基本上我只是用这一段来替换我的 var 中的一个字符串,但我无法让嵌套延迟扩展正常工作。这甚至可能吗? set replace=!replace:!search!=!replaceVal!! 我知道执
如何使用 ffmpeg 对一批视频文件进行编码,使用相同的设置? 我找到了 one-line solution将当前文件夹中的 .avi 文件转换为 .mov。请注意,我要编码 .mov -> .mo
我正在尝试制作一个批处理文件,每次循环时都会将变量增加 1,然后检查变量是否等于 5,如果不是,则再次循环。我知道这可能有一个 while 循环,但我不知道如何做到这一点,我现在只是享受学习 Batc
我正在尝试创建一个循环,读取多个 CSV 文件,这些文件都具有相同类型的气温数据。但是,我想跳过数据上方的行。这些是数据集中的“警报”。每个文件可能有不同数量的警报,因此要跳过不同数量的行。见下文:
因此,我正在批量创建一个Mail程序,而消息传递部分出现了问题。 消息传递部分是无限循环。 当我输入多个单词时,它会崩溃。 这是代码。请帮忙! :rep set line= set /p line=
我是一名优秀的程序员,十分优秀!