Java多线程下载文件

2023-11-12

Java多线程下载文件

优化:合理利用服务器资源,将资源利用最大化,加快下载速度

一般有两种方式:

  • 线程池里面有N个线程,多线程下载单个文件,将网络路径的文件流切割成多快,每个线程下载一小部分,然后写入到文件里面,组成一个文件
  • 当有很多个文件需要下载的时候,调用某个方法,有个线程池,线程池大小假定是10,当有10个文件过来的时候,每个线程去下载一个文件即可

多线程如果只知道Thread和Runnable,那你就Out了,Java1.5以后有个concurrent包,就是Java并发编程。

N个线程下载单个文件

定义一个单个线程下载的部分

public class DownloadWithRange implements Runnable {
    private String urlLocation;

    private String filePath;

    private long start;

    private long end;

    DownloadWithRange(String urlLocation, String filePath, long start, long end) {
        this.urlLocation = urlLocation;
        this.filePath = filePath;
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {
        try {
            HttpURLConnection conn = getHttp();
            conn.setRequestProperty("Range", "bytes=" + start + "-" + end);

            File file = new File(filePath);
            RandomAccessFile out = null;
            if (file != null) {
                out = new RandomAccessFile(file, "rw");
            }
            out.seek(start);
            InputStream in = conn.getInputStream();
            byte[] b = new byte[1024];
            int len = 0;
            while ((len = in.read(b)) >= 0) {
                out.write(b, 0, len);
            }
            in.close();
            out.close();
        } catch (Exception e) {
            e.getMessage();
        }

    }

    public HttpURLConnection getHttp() throws IOException {
        URL url = null;
        if (urlLocation != null) {
            url = new URL(urlLocation);
        }
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(5000);
        conn.setRequestMethod("GET");

        return conn;
    }

}

定义线程Pool

public class DownloadFileWithThreadPool {

    public void getFileWithThreadPool(String urlLocation, String filePath, int poolLength) throws IOException {
        ExecutorService threadPool = Executors.newCachedThreadPool();

        long len = getContentLength(urlLocation);
        System.out.println(len);
        for (int i = 0; i < poolLength; i++) {
            long start = i * len / poolLength;
            long end = (i + 1) * len / poolLength - 1;
            if (i == poolLength - 1) {
                end = len;
            }
            System.out.println(start+"---------------"+end);
            DownloadWithRange download = new DownloadWithRange(urlLocation, filePath, start, end);
            threadPool.execute(download);
        }
        threadPool.shutdown();
    }

    public static long getContentLength(String urlLocation) throws IOException {
        URL url = null;
        if (urlLocation != null) {
            url = new URL(urlLocation);
        }
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(5000);
        conn.setRequestMethod("GET");
        long len = conn.getContentLength();

        return len;
    }

}

测试下载单个文件

public static void main(String[] args) {
        Date startDate = new Date();
        DownloadFileWithThreadPool pool = new DownloadFileWithThreadPool();
        try {
            pool.getFileWithThreadPool("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3", "D:\\1.mp3", 100);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(new Date().getTime() - startDate.getTime());
    }

测试发现,我目前的网速,下载一个3.6M左右mp3音频文件,使用多线程,下载大概需要800ms左右,而不使用多线程,则需要1600ms以上,有时候甚至3000ms

多文件下载,单文件单线程

/**
 * @author Nick 带线程池的文件下载类,线程大小10
 *      文件数少的情况下,体现不大
 * @version V1.0.0
 * @Date 2017/8/2 20:43
 */
public class FileDownConnManager {

    private static final Logger logger = LoggerFactory.getLogger(FileDownConnManager.class);

    private static final FileDownConnManager connManager = new FileDownConnManager();

    private static ExecutorService executorService = Executors.newFixedThreadPool(10); //10个线程跑

    public static FileDownConnManager getDefaultManager() {
        return connManager;
    }

    public static byte[] fileDown(final String netURL) throws ExecutionException, InterruptedException {
        Future<byte[]> future = executorService.submit(new Callable<byte[]>() {
            @Override
            public byte[] call() throws Exception {
                Date date = new Date();
                URL url;
                byte[] getData = new byte[0];
                InputStream is = null;
                try {
                    url = new URL(netURL);
                    URLConnection connection = url.openConnection();
                    is = connection.getInputStream();
                    getData = readInputStream(is);

                } catch (IOException e) {
                    logger.error("从URL获得字节流数组失败 " + ExceptionUtils.getMessage(e));
                } finally {
                    try {
                        is.close();
                    } catch (IOException e) {
                        logger.error("从URL获得字节流数组流释放失败 " + ExceptionUtils.getMessage(e));
                    }
                }
                return getData;
            }
        });
        return future.get();
    }

}

测试

@Test
    public void test2() throws ExecutionException, InterruptedException, IOException {
        long time1 = System.currentTimeMillis();
        for(int i = 0; i < 15; i++) {
            byte[] by1 = FileDownConnManager.fileDown("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3");
            FileUtils.writeByteArrayToFile(new File("D:\\test2_"+i+".mp3"), by1);
        }
        System.out.println(System.currentTimeMillis() - time1);
    }

    @Test
    public void test3() throws IOException {
        long time1 = System.currentTimeMillis();
        for(int i = 0; i < 15; i++) {
            byte[] by1 = FileFromUrlUtil.getInputStreamFromUrl("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3");
            FileUtils.writeByteArrayToFile(new File("D:\\test3_"+i+".mp3"), by1);
        }
        System.out.println(System.currentTimeMillis() - time1);
    }

结论:同样的文件,测试发现,在一定数量(文件数量大概15)的文件下载的时候,每个文件是3.6M的情况下,使用多线程大概时间是30xxxms,而不实用多线程大概是38xxxms,还是有一定时间上的提升

此处的测试还是会存在一定的误差,因为ExecutorService线程池的创建是需要时间,类的初始化也是需要一定的时间,假如在Web应用中,第一次创建出来了,第二次不需要创建,优势还是稍微能够体现出来!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多线程下载文件 的相关文章

  • Qt的Tcp服务器多线程编程-附带代码展示

    Qt的Tcp服务器多线程编程 附带代码展示 该程序主要实现tcp服务器如何使用多线程的方式来连接多个客户端 此文章没有实现客户端的多线程编程 创建子线程时需要注意的点 1 子线程与主线程之间交互数据时 应采用信号槽的方式 2 子线程中实例化
  • java多线程中synchronized同步代码块执行问题

    在高洪岩老师的 java多线程编程核心技术 一书的用同步代码块解决同步方法的弊端一节中 p76页 有这样一句话 当一个线程访问object的一个synchronized同步代码块时 另一个线程依然可以访问该object对象中的非synchr
  • 无线传感网必知必会

    一 填空题 传感器网络三大基本要素 传感器 感知对象 用户 观测者 传感器节点的基本功能模块包括 数据采集模块 数据处理和控制模块 通信模块 供电模块 四个 其中 通信模块 能量消耗最大 传感器节点通信模块的工作模式有 发送 接收 空闲 睡
  • 静态代理模式

    package com kuang Demo04 静态代理总结 真实对象和代理对象都要实现同一个接口 代理对象要代理真实对象 好处 代理对象可以做很多真实对象做不了的事情 真实对象可以专注做自己的事情 public class Static
  • thread_Timer(线程中定时器)

    package com gzhs zsd thread import java util Date import java util Timer import java util TimerTask Timer定时器运用 author 谢泽
  • futureTask RunnableFuture Future 三者关系认知

    对于这三者首先我们看下源码 之后在分别写几个demo讲解下用法 public interface RunnableFuture
  • 对聊天室的优化&常用参数配置

    优化处1 编码和解码 编码解码用的是JDK 对象与数组的转换 这种虽然简单 但是效率不高 现在需要支持更多的序列化算法 就需要改进 抽取一个接口 Serializer 用以支持 序列化和 反序列化 package com zhao prot
  • 08C++11多线程编程之unique_lock类模板

    08C 11多线程编程之unique lock类模板 前述 如果看懂了该篇文章 你对unique lock可以说随便使用 并且可以只看第5点的总结即可 1 unique lock概念 当不加参数时 和lock guard一样能自动上锁解锁
  • java数据迁移程序

    环境 mysql 目标 亿级数据迁移 最终耗时 1 2小时 服务器更佳 建议在晚上或者没人访问的情况下操作 思路 1 不能一下将所有数据 导入到目标数据表 耗时太久 且占用资源 所有就用程序批量执行 每次执行一个范围段 比如第一个线程 1
  • 关于Semaphore信号量的源码解读

    Semaphore的简单使用 利用Semaphore可以实现对线程数量的控制 比如如下的代码 class SemaphoreTest public static void main String args Semaphore semapho
  • 并发编程(二)——内存模型

    前言 欢迎大家一起来学习多线程 大家一起来学习吧 并发编程 一 多线程快速入门 并发编程 二 内存模型 并发编程 三 多线程之间如何实现通讯 并发编程 四 JUC并发包常用方法介绍 并发编程 五 线程池及原理剖析 并发编程 六 java中锁
  • Linux系统编程:多线程交替打印ABC

    引言 分享关于线程的一道测试题 因为网上基本都是Java的解决方法 决定自己写一篇来记录一下线程的学习 问题描述 编写一个至少具有三个线程的程序 称之为线程 A B 和 C 其中线程 A 输出字符 A 线程 B 输出字符 B 线程 C 输出
  • 55黑马QT笔记之关闭子线程

    55黑马QT笔记之关闭子线程 1 这里为什么要单独写多一篇文章来说线程的关闭呢 主要是想让大家提升印象 养成资源回收的好习惯 任何时候都要想起开辟过的内存回收 这里的关闭子线程上一篇也写到了 就是利用关闭窗口时调用槽函数回收掉 2 具体步骤
  • Java多线程 - 线程池常用容量设置

    线程执行方式 线程的执行是由CPU进行调度的 一个CPU在 同一时刻只会执行一个线程 操作系统利用了时间片轮转的方式 CPU给每个任务都服务一定的时间 然后把当前任务的状态保存下来 再加载下一个任务的状态后 继续服务下一个任务 任务的保存及
  • 02Linux下C语言锁的学习之Linux下的读写锁

    02Linux下C语言锁的学习之Linux下的读写锁 概述 下面的锁的意思均是代表读写锁 读写锁的特性 1 若一把锁被一个线程以读方式锁住 当其它线程以读方式上锁的话 那么可以上锁成功 2 若一把锁被一个线程以写方式锁住 当其它线程以读或者
  • 多线程下载文件(支持暂停、取消、断点续传)

    多线程下载文件 支持暂停 取消 断点续传 多线程同时下载文件即 在同一时间内通过多个线程对同一个请求地址发起多个请求 将需要下载的数据分割成多个部分 同时下载 每个线程只负责下载其中的一部分 最后将每一个线程下载的部分组装起来即可 涉及的知
  • JAVA并发:线程安全与Synchorinzed

    1 什么是线程安全问题 线程的合理使用能够提升程序的处理性能 主要有两个方面 第一个是能够利用多核 cpu 以及超线程技术来实现线程的并行执行 第二个是线程的异步化执行相比于同步执行来说 异步执行能够很好的优化程序的处理性能提升并发吞吐量
  • ScheduledThreadPoolExecutor周期定时任务异常处理踩坑的问题!!

    问题原因 在公司写项目的时候 有一个周期定时任务的需求 就想着阿里巴巴开发手册里不是说不能用Executors去创建线程池 因为存在如下问题 FixedThreadPool和SingleThreadPool 允许的请求队列长度为 Integ
  • VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题...

    VC9编译的程序在没有装过VC9 确切的说是 Net Framework3 5 的机器上运行时 如果提示 由于应用程序配置不正确 应用程序未能启动 重新安装应用程序可能会纠正这个问题 这个错误 那么就说明该程序动态链接了VC9的运行时库 如
  • 线程安全的集合类

    Java中提供了许多集合类 其中有的是线程安全的 有的是线程不安全的 线程安全的集合类有 1 Vector Vector类实现了一个 动态数组 与ArrayList相似 但Vector是同步访问的 2 Stack Stack是Vector的

随机推荐

  • websocket实现聊天室(一)

    最近接到一个聊天室的任务 之前在学校完全没有接触过这方面的需求 在网上查找资料后 基本确定了实现方案 现在就开始着手学习 在此记录一下遇到的问题 初识websocket 在简单了解websocket后 我觉得与http请求类似 不过webs
  • 读取g2o 文件的python实现

    可以读取2D 和 3D的 g2o 文件 并可以把四元数的位姿转换为节点和边数据 import argparse import numpy as np import pyquaternion File Format Vertex 2D Rob
  • H.264 标准简介

    JVT Joint Video Team 视频联合工作组 于2001年12月在泰国Pattaya成立 它由ITU T和ISO两个国际标准化组织的有关视频编码的专家联合组成 JVT的工作目标是制定一个新的视频编码标准 以实现视频的高压缩比 高
  • Visual Studio编译出来的程序无法在其它电脑上运行

    在其它电脑 比如Windows Server 2012 上运行Visual Studio编译出来的应用程序 结果报错 无法启动此程序 因为计算机中丢失VCRUNTIME140 dll 尝试重新安装该程序以解决此问题 解决方法 属性 gt 配
  • PNP和NPN磁感应开关有什么区别

    1 我们以磁性开关为例 先要搞清楚PNP NPN 表示的意思是什么 P表示正 N表示负 PNP表示平时为高电位 信号到来时信号为负 NPN表示平时为低电位 信号到来时信号为高电位输出 接近开关和光电开关只是检测电路不同输出相同 至于PLC接
  • Spring 提示:无法找到元素 'aop:aspectj-autoproxy'

    问题描述 org springframework beans factory xml XmlBeanDefinitionStoreException Line 18 in XML document from class path resou
  • 程序员从初级到中级10个秘诀

    新闻来源 techrepublic comJustin James曾发表过一篇博文 10 tips for advancing from a beginner to an intermediate developer 为我们分享如何才能完成
  • syskey (win7启动密码)加密和破解方法

    1 什么是syskey Syskey是NT Service Pack 3中带的一个工具 用来保护SAM数据库不被离线破解 用过去的加密机制 如果攻击者能够得到一份加密过的SAM库的拷贝 他就能够在自己的机器上来破解用户口令 2 如何开启sy
  • [Json依赖] JSONObject的依赖包

  • Windos10专业版开启远程桌面协助

    我需要控制局域网的电脑 这台电脑是win10专业版 搜索 远程桌面设置 进入后启动远程桌面设置 然后发现当前用户已经有访问权 当前用户没有密码 那么远程失败 解决方法是 按win r 输入GPEDIT MSC 计算机配置 gt 安全设置 g
  • 前端框架React

    前端框架React 组件基础 React事件机制 哪些方法会让React重新渲染 render会做什么 React类组件和函数组件 React高阶组件 和普通组件的区别 适用场景 React受控组件和非受控组件 React有状态组件和无状态
  • java enum compare_Java Compare Enum value

    In Java you can use operator to compare Enum value 1 Java Enum example Language java package com mkyong java public enum
  • 啥?简单的题都不会,可咋整呢?

    目录 一 寻找原因 二 寻找解决方法 三 常见的刷题网站 刷题技巧 明明自觉学会了不少知识 可真正开始做题时 却还是出现了 一支笔 一双手 一道力扣 Leetcode 做一宿 的窘境 你是否也有过这样的经历 题型不算很难 看题解也能弄明白
  • Python3获取股票行情数据(中国个股/中国指数/全球指数)

    usr local bin python3 coding utf 8 source http www cnblogs com txw1958 import os io sys re time json base64 import webbr
  • Js常用面试题目知识整理

    Js代码题 1 千分位 题目 要求返回参数数字的千分位分隔符字符串 思路 在字符串长度不确定的情况下 可以使用递归 comma number 1000 是获取数字最后三位 将其放在返回值的最后面 并且在前面加一个逗号 comma Math
  • freenom域名申请教程

    freenom域名申请教程 1 注册 申请域名 打开freenom官网 注册一个账户 注意 如果没有明显的注册按钮 可以通过如下方式同时申请域名和注册账户 打开域名申请 不用注册 选择好了域名之后 点击Checkout 选择免费期限 最长的
  • shell脚本编程 实例讲解(键盘输入三个数字,按照从大到小的书顺序输出)

    1 键盘输入三个数字 按照从大到小的书顺序输出 排序题 a b c 2 10 9 a 2 b 10 c 9 第一步 两两相互进行比较 比较三次 第二步 不论谁大谁小 最后都输出 a b c 从大从小 a永远存储的都是最大值 a和b进行比较
  • 笔记&代码

    可视化前三步走 数据类型 分析目的 实现工具 2 1 类别数据可视化 显示各类别的绝对频数及百分比等 条形图 饼图等 2 1 1 条形图及其变种 垂直条形图 类别在x轴 水平条形图 类别在y轴 简单条形图 并列条形图 堆叠条形图 1 简单条
  • 前端知识——css 之 flex 布局

    目录 一 认识 flex 布局 1 flex 布局的重要概念 二 flex 相关属性 1 flex container 中的属性 1 1 flex direction item 的排布方向 1 2 flex wrap 排布是否换行 1 3
  • Java多线程下载文件

    Java多线程下载文件 优化 合理利用服务器资源 将资源利用最大化 加快下载速度 一般有两种方式 线程池里面有N个线程 多线程下载单个文件 将网络路径的文件流切割成多快 每个线程下载一小部分 然后写入到文件里面 组成一个文件 当有很多个文件