java多线程下载实例详解

Java多线程下载实例详解

本文将介绍Java多线程下载的实现方法和步骤,并提供两个示例说明。

实现步骤

Java多线程下载的实现步骤如下:

  1. 获取需要下载的文件的URL地址。
  2. 创建多个线程,每个线程负责下载文件的不同部分。
  3. 启动多个线程,通过HTTP请求下载各自负责的文件部分。
  4. 合并下载完成的文件部分。
  5. 完成文件下载。

示例一:Java多线程文件下载

以下示例是一个基于Java多线程的文件下载器,该程序支持并发下载多个文件,每个文件都会分成多个线程进行下载。

import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadFileDownloader {

    private String url;  // 下载链接
    private String fileName;  // 本地保存文件名
    private String fileSavePath; // 文件保存目录
    private int threadNum;  // 线程数量

    public MultiThreadFileDownloader(String url, String fileSavePath, String fileName, int threadNum) {
        this.url = url;
        this.fileSavePath = fileSavePath;
        this.fileName = fileName;
        this.threadNum = threadNum;
    }

    public void download() throws Exception {
        URL fileUrl = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();

        // 获取下载文件长度
        int fileSize = connection.getContentLength();
        connection.disconnect();

        // 保存文件夹不存在则创建
        File saveDir = new File(fileSavePath);
        if (!saveDir.exists()) {
            saveDir.mkdirs();
        }

        // 计算每个线程要下载的字节数
        int blockSize = fileSize / threadNum + 1;

        // 创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 创建下载任务
        DownloadTask[] downloadTasks = new DownloadTask[threadNum];
        for (int i = 0; i < threadNum; i++) {
            int startIndex = i * blockSize;  // 计算下载起始位置
            int endIndex = (i + 1) * blockSize - 1;  // 计算下载结束位置

            // 最后一个线程下载到文件末尾
            if (i == threadNum - 1) {
                endIndex = fileSize;
            }

            // 创建下载任务
            DownloadTask downloadTask = new DownloadTask(url, fileSavePath, fileName, startIndex, endIndex);
            downloadTasks[i] = downloadTask;

            // 将下载任务交给线程池执行
            executorService.execute(downloadTask);
        }

        // 等待所有线程下载完成
        for (DownloadTask downloadTask : downloadTasks) {
            downloadTask.waitFinish();
        }

        // 合并下载文件
        mergeDownloadFile();

        // 关闭线程池
        executorService.shutdown();
    }

    // 合并下载文件
    private void mergeDownloadFile() throws Exception {
        // 创建下载文件
        File downloadFile = new File(fileSavePath + fileName);
        RandomAccessFile randomAccessFile = new RandomAccessFile(downloadFile, "rw");

        // 遍历所有下载文件块,并将文件块内容依次写入下载文件
        for (int i = 0; i < threadNum; i++) {
            File downloadTempFile = new File(fileSavePath + fileName + ".temp" + i);
            RandomAccessFile tempFile = new RandomAccessFile(downloadTempFile, "r");

            byte[] buffer = new byte[1024];
            int length;
            while ((length = tempFile.read(buffer)) != -1) {
                randomAccessFile.write(buffer, 0, length);
            }

            tempFile.close();

            // 删除临时文件
            downloadTempFile.delete();
        }

        randomAccessFile.close();
    }

    // 下载任务
    private class DownloadTask implements Runnable {
        private String url;  // 下载链接
        private String fileSavePath; // 本地保存路径
        private String fileName; // 保存文件名
        private int startIndex;  // 起始下载位置
        private int endIndex;  // 结束下载位置
        private boolean isFinish = false; // 是否下载完成

        public DownloadTask(String url, String fileSavePath, String fileName, int startIndex, int endIndex) {
            this.url = url;
            this.fileSavePath = fileSavePath;
            this.fileName = fileName;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            try {
                URL fileUrl = new URL(this.url);
                HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();

                // 设置下载文件的范围
                connection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

                byte[] buffer = new byte[1024];
                int length;
                int totalLength = 0;

                // 创建临时文件
                File file = new File(fileSavePath + fileName + ".temp" + Thread.currentThread().getId());
                RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

                // 获取输入流并将数据写入临时文件
                while ((length = connection.getInputStream().read(buffer)) != -1) {
                    randomAccessFile.write(buffer, 0, length);
                    totalLength += length;
                }

                randomAccessFile.close();
                connection.disconnect();

                // 标记下载任务已经完成
                isFinish = true;

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 等待下载任务完成
        public void waitFinish() throws InterruptedException {
            while (!isFinish) {
                Thread.sleep(100);
            }
        }
    }
}

示例二:Java多线程图片下载

以下示例展示了如何使用Java多线程下载图片,并将其保存到本地磁盘中。

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class MultiThreadImageDownloader {

    public static void downloadImage(String imageUrl, String savePath) {
        try {
            // 获取连接
            URL url = new URL(imageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // 获取文件长度
            int fileSize = connection.getContentLength();
            System.out.println("文件大小为:" + fileSize);

            // 分配线程下载
            int threadNum = 4;
            int blockSize = fileSize / threadNum + 1;
            FileDownloadThread[] threads = new FileDownloadThread[threadNum];

            // 创建文件夹
            File dir = new File(savePath);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            // 分别开启多个线程下载不同的文件块
            for (int i = 0; i < threadNum; i++) {
                int start = i * blockSize;
                int end = (i + 1) * blockSize - 1;
                if (i == threadNum - 1) {
                    end = fileSize - 1;
                }
                System.out.println("线程 " + i + " 下载:" + start + " ~ " + end + " 字节");

                FileDownloadThread thread = new FileDownloadThread(url, start, end, new File(savePath + "/" + getFileName(imageUrl)), savePath);
                thread.start();
                threads[i] = thread;
            }

            // 等待所有的线程下载完成
            for (int i = 0; i < threadNum; i++) {
                threads[i].join();
            }

            System.out.println("文件下载完成");

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("下载文件出现异常");
        }
    }

    // 根据图片URL获取图片名称
    public static String getFileName(String imageUrl) {
        int index = imageUrl.lastIndexOf("/");
        return imageUrl.substring(index + 1);
    }

    // 下载线程
    public static class FileDownloadThread extends Thread {
        private URL url;  // 下载链接
        private int start; // 线程下载开始位置
        private int end; // 线程下载结束位置
        private File file; // 本地保存的文件
        private String savePath; // 文件保存目录

        public FileDownloadThread(URL url, int start, int end, File file, String savePath) {
            this.url = url;
            this.start = start;
            this.end = end;
            this.file = file;
            this.savePath = savePath;

            if (!file.exists()) {
                try {
                    FileOutputStream out = new FileOutputStream(file);
                    byte[] buf = new byte[end - start + 1];
                    out.write(buf);
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void run() {
            try {
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setRequestProperty("Range", "bytes=" + start + "-" + end);

                InputStream in = connection.getInputStream();
                byte[] buf = new byte[1024];
                int len = 0;
                RandomAccessFile out = new RandomAccessFile(file, "rw");
                out.seek(start);
                while ((len = in.read(buf)) != -1) {
                    out.write(buf, 0, len);
                }
                out.close();
                in.close();
                connection.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

以上就是Java多线程下载的实现方法和示例。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java多线程下载实例详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 详解JUC并发编程之锁

    详解JUC并发编程之锁 什么是锁 锁是Java并发编程中用于控制多个线程访问共享资源的机制。在多线程环境下,由于线程运行的不确定性,多个线程可能会同时访问同一个共享资源,导致数据不一致、程序崩溃等问题。锁机制可以保证同一时刻只有一个线程能够访问共享资源,从而达到并发安全的目的。 Java中的锁分类 Java中的锁主要分为两类:内部锁(synchronized…

    多线程 2023年5月17日
    00
  • C++11学习之多线程的支持详解

    C++11学习之多线程的支持详解 在C++11标准中,多线程支持成为了一个正式的标准库,并引入了一些新的概念和类型,如线程、互斥锁、条件变量等,以及一些用于控制线程行为的函数和类。 下面我们来详细讲解多线程的支持。 线程 在线程头文件<thread>中定义了线程类std::thread,用于创建和控制线程。线程类的构造函数接收一个可调用对象,并执…

    多线程 2023年5月17日
    00
  • Java多线程之Future设计模式

    下面是详细的讲解“Java多线程之Future设计模式”的完整攻略。 什么是Future设计模式 Future设计模式是一种Java多线程技术,它可以在一个线程中异步执行某些任务,然后在未来的某个时间点获取任务的结果。通常情况下,我们会使用Future设计模式来加快应用程序的响应速度,因为它可以将应用程序的某些任务异步化,使得这些任务的执行速度不会影响其他任…

    多线程 2023年5月16日
    00
  • c++11&14-多线程要点汇总

    C++11&14-多线程要点汇总 在C++11和C++14标准中,多线程相关的API得到了极大的增强和改善,本文将总结介绍其中一些重要的关键点。 1. std::thread std::thread是C++11中线程的关键类型,用于创建和管理线程。可以使用std::thread的构造函数来创建一个新的线程: #include <iostream…

    多线程 2023年5月17日
    00
  • 如何使用JCTools实现Java并发程序

    JCTools是一组相对较新的Java并发编程工具,提供了一些高性能的队列及其他并发数据结构,适合在高并发、低延迟的场景下使用。下面将详细讲解如何使用JCTools实现Java并发程序。 安装JCTools 使用Gradle或者Maven构建项目,添加以下依赖项即可使用JCTools: // Gradle compile group: "org.j…

    多线程 2023年5月17日
    00
  • Java多线程的原子性,可见性,有序性你都了解吗

    当多个线程并发执行同一段代码时,有可能会出现线程安全问题。而Java多线程的原子性,可见性和有序性是解决这些线程安全问题的关键。 原子性:原子性指的是一个操作不可中断,要么全部执行成功,要么全部执行失败。Java的基本数据类型的读取和赋值都是具有原子性的。但当多个线程同时对同一个变量进行运算时,就需要考虑原子性的问题。 示例说明: public class …

    多线程 2023年5月16日
    00
  • 五种Java多线程同步的方法

    下面是关于“五种Java多线程同步的方法”的详细攻略。 介绍 在并发编程中,线程同步是非常重要的。Java中有五种常见的线程同步方法,包括synchronized关键字、Lock接口、Semaphore、CountDownLatch和CyclicBarrier。下面将对这五种方法做详细讲解。 1. synchronized关键字 synchronized关键…

    多线程 2023年5月17日
    00
  • java多线程编程之Synchronized关键字详解

    Java多线程编程之Synchronized关键字详解 什么是Synchronized关键字 Synchronized是一种Java中的关键字,可以将一段代码标记为“临界区”,保证多个线程在执行该代码时不会发生冲突,保证数据的正确性。 Synchronized关键字的用法 Synchronized关键字可以用在方法或代码块上。 用在方法上 public sy…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部