Java多线程Demo简介

多线程文件下载管理器

1. 环境准备
  • Java 8 或更高版本
  • 任何文本编辑器或IDE(如IntelliJ IDEA或Eclipse)
2. 需求分析
  • 功能需求
    • 支持从多个URL同时下载文件
    • 显示下载进度
    • 异常处理和重试机制
3. 实现步骤
3.1 创建Downloader

这个类实现了Runnable接口,用于下载单个文件。

package com.example.downloadmanager;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

public class Downloader implements Runnable {
    private String fileUrl;
    private String saveDir;

    public Downloader(String fileUrl, String saveDir) {
        this.fileUrl = fileUrl;
        this.saveDir = saveDir;
    }

    @Override
    public void run() {
        try {
            downloadFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void downloadFile() throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        int responseCode = httpURLConnection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream inputStream = httpURLConnection.getInputStream();
            downloadToFile(inputStream);
        } else {
            System.out.println("Failed to download " + fileUrl);
        }
        httpURLConnection.disconnect();
    }

    private void downloadToFile(InputStream inputStream) throws IOException {
        File file = new File(saveDir);
        if (!file.exists()) {
            file.mkdirs();
        }
        file = new File(file, fileUrl.substring(fileUrl.lastIndexOf('/') + 1));
        OutputStream outputStream = new FileOutputStream(file);

        byte[] buffer = new byte[1024];
        int bytesRead;
        long totalBytesRead = 0;
        long fileLength = inputStream.available();

        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            totalBytesRead += bytesRead;
            double progress = 100.0 * totalBytesRead / fileLength;
            System.out.printf("Downloading %s: %.2f%%\n", fileUrl, progress);
        }

        outputStream.close();
        inputStream.close();
        System.out.println("Download completed for " + fileUrl);
    }
}
3.2 创建DownloadManager

这个类使用ExecutorService来管理多个下载任务。

package com.example.downloadmanager;

import java.io.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DownloadManager {
    private ExecutorService executorService;
    private int threadPoolSize;

    public DownloadManager(int threadPoolSize) {
        this.threadPoolSize = threadPoolSize;
        this.executorService = Executors.newFixedThreadPool(threadPoolSize);
    }

    public void downloadFiles(String saveDir, String... fileUrls) {
        for (String fileUrl : fileUrls) {
            Runnable downloader = new Downloader(fileUrl, saveDir);
            executorService.execute(downloader);
        }
    }

    public void shutdown() {
        executorService.shutdown();
    }
}
3.3 测试DownloadManager

创建一个主类来测试下载管理器。

package com.example.downloadmanager;

public class Main {
    public static void main(String[] args) {
        String saveDirectory = "downloads";
        DownloadManager downloadManager = new DownloadManager(5); // 5线程同时下载

        String[] urls = {
            "http://example.com/file1.pdf",
            "http://example.com/file2.pdf",
            // 更多URL...
        };

        downloadManager.downloadFiles(saveDirectory, urls);

        // 优雅关闭下载管理器
        Runtime.getRuntime().addShutdownHook(new Thread(() -> downloadManager.shutdown()));
    }
}
4. 测试和验证

运行Main类,观察控制台输出,确保文件正在被下载,并且下载进度被正确显示。

5. 总结

这个案例展示了如何使用Java多线程来提高文件下载的效率。通过DownloadManager类,我们可以同时启动多个下载任务,每个任务都是一个Downloader线程。使用ExecutorService来管理线程池,可以简化线程的创建和调度。

此外,这个案例还演示了如何处理文件I/O操作和网络请求,以及如何优雅地关闭资源。在实际应用中,您可能需要添加更多的功能,比如下载暂停和恢复、下载队列管理、更复杂的异常处理和重试机制等。

接下来,我们将扩展我们的多线程文件下载管理器案例,增加以下特性:

  1. 下载任务的取消功能:允许用户取消正在进行的下载任务。
  2. 下载任务的优先级:允许用户设置下载任务的优先级。
  3. 下载速度限制:限制每个下载任务的速度。

扩展特性实现

1. 增加下载任务控制

首先,我们需要为Downloader类增加取消功能。

package com.example.downloadmanager;

public class Downloader implements Runnable {
    // ... existing fields and methods ...

    private volatile boolean isCancelled = false;

    @Override
    public void run() {
        try {
            if (isCancelled) return; // 如果任务被取消,则直接返回
            downloadFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void cancel() {
        isCancelled = true;
    }

    // ... rest of the Downloader class ...
}
2. 增加任务优先级

我们需要修改DownloadManager来支持任务优先级。

package com.example.downloadmanager;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DownloadManager {
    private ThreadPoolExecutor executorService;
    private int threadPoolSize;

    // 使用优先级队列
    private PriorityBlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>();

    public DownloadManager(int threadPoolSize) {
        this.threadPoolSize = threadPoolSize;
        // 创建具有优先级任务队列的线程池
        this.executorService = new ThreadPoolExecutor(
                threadPoolSize,
                threadPoolSize,
                0L,
                TimeUnit.MILLISECONDS,
                taskQueue
        );
    }

    public void downloadFiles(String saveDir, DownloadTask... tasks) {
        for (DownloadTask task : tasks) {
            executorService.execute(task);
        }
    }

    // ... rest of the DownloadManager class ...
}

定义DownloadTask类,它扩展了Downloader类并实现了Comparable接口以支持优先级。

package com.example.downloadmanager;

public class DownloadTask extends Downloader implements Comparable<DownloadTask> {
    private int priority;

    public DownloadTask(String fileUrl, String saveDir, int priority) {
        super(fileUrl, saveDir);
        this.priority = priority;
    }

    // Implement compareTo to define the order of task execution based on priority
    @Override
    public int compareTo(DownloadTask other) {
        return Integer.compare(other.priority, this.priority);
    }

    // ... rest of the DownloadTask class including cancel method ...
}
3. 限制下载速度

为了限制下载速度,我们需要在DownloaderdownloadToFile方法中增加延时。

package com.example.downloadmanager;

public class Downloader {
    // ... existing fields ...

    private static final int BUFFER_SIZE = 1024;
    private static final int MAX_BYTES_PER_SECOND = 1024; // 1 KB/s

    // ... existing methods ...

    private void downloadToFile(InputStream inputStream) throws IOException {
        // ... existing code ...

        long bytesToSleep = (long) (BUFFER_SIZE / MAX_BYTES_PER_SECOND) * 1000 / BUFFER_SIZE;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            totalBytesRead += bytesRead;
            // 确保不超过下载速度限制
            if (bytesRead >= BUFFER_SIZE) {
                try {
                    Thread.sleep(bytesToSleep);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            // ... rest of the method ...
        }
    }
}
4. 测试和验证

更新Main类以使用新的DownloadManagerDownloadTask

package com.example.downloadmanager;

public class Main {
    public static void main(String[] args) {
        String saveDirectory = "downloads";
        DownloadManager downloadManager = new DownloadManager(5);

        DownloadTask highPriorityTask = new DownloadTask("http://example.com/high_priority_file.pdf", saveDirectory, 1);
        DownloadTask lowPriorityTask = new DownloadTask("http://example.com/low_priority_file.pdf", saveDirectory, 5);

        downloadManager.downloadFiles(saveDirectory, highPriorityTask, lowPriorityTask);

        // Allow the user to cancel a task or limit download speed
        // highPriorityTask.cancel();

        // ... rest of the Main class ...
    }
}
5. 总结

通过这些扩展,我们的多线程文件下载管理器现在支持任务取消、优先级排序和下载速度限制。这些特性使得下载管理器更加灵活和强大,能够更好地满足用户的需求。

  • 任务取消:通过设置isCancelled标志,用户可以随时取消正在执行的下载任务。
  • 任务优先级:通过实现Comparable接口和使用优先级队列,下载任务可以根据用户定义的优先级进行排序和执行。
  • 下载速度限制:通过在每次写入后添加适当的延时,可以限制每个下载任务的速度。

这些特性的实现展示了Java多线程编程的强大能力,以及如何使用Java的并发工具来构建复杂的多线程应用程序。在实际应用中,您可能还需要考虑更多的异常处理和资源管理策略,以确保应用程序的稳定性和效率。

当然,我们还可以继续添加以下特性:

  1. 用户界面:提供一个简单的文本界面或图形界面,让用户能够启动、取消下载任务,并查看下载状态。
  2. 持久化下载状态:在应用重启后能够恢复未完成的下载任务。
  3. 日志记录:记录下载过程中的关键信息,便于问题诊断和性能监控。
  4. 配置文件:使用配置文件来管理下载设置,如线程池大小、下载速度限制等。

扩展特性实现

1. 用户界面

这里我们使用Java Swing来创建一个简单的图形用户界面。

package com.example.downloadmanager.ui;

import com.example.downloadmanager.DownloadManager;
import com.example.downloadmanager.DownloadTask;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class DownloadUI extends JFrame {
    private DownloadManager downloadManager;
    private JList<String> downloadsList;
    private JButton startButton;
    private JButton cancelButton;
    private JProgressBar downloadProgress;

    public DownloadUI(DownloadManager downloadManager) {
        this.downloadManager = downloadManager;
        initializeUI();
    }

    private void initializeUI() {
        setTitle("Download Manager");
        setSize(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        downloadsList = new JList<>();
        downloadsList.setFixedHeight(100);
        add(new JScrollPane(downloadsList));

        startButton = new JButton("Start Download");
        startButton.addActionListener(new StartDownloadAction());
        add(startButton);

        cancelButton = new JButton("Cancel Download");
        cancelButton.addActionListener(new CancelDownloadAction());
        add(cancelButton);

        downloadProgress = new JProgressBar();
        add(downloadProgress);
    }

    private class StartDownloadAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            // 从UI获取下载链接和设置,然后添加到下载管理器
            // 假设使用了一个JTextField来输入下载链接
            String fileUrl = "http://example.com/file.pdf";
            String saveDir = System.getProperty("user.home");
            int priority = 1; // 从UI获取优先级
            DownloadTask task = new DownloadTask(fileUrl, saveDir, priority);
            downloadManager.downloadFiles(saveDir, task);
            downloadsList.add(fileUrl);
        }
    }

    private class CancelDownloadAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            // 从列表中获取选中的下载任务并取消
            Object[] selectedValues = downloadsList.getSelectedValues();
            if (selectedValues != null && selectedValues.length > 0) {
                for (Object value : selectedValues) {
                    // 假设我们有一种方法来找到并取消任务
                    downloadManager.cancelDownload((String) value);
                }
            }
        }
    }

    public static void main(String[] args) {
        DownloadManager manager = new DownloadManager(5);
        SwingUtilities.invokeLater(() -> {
            DownloadUI ui = new DownloadUI(manager);
            ui.setVisible(true);
        });
    }
}
2. 持久化下载状态

为了实现下载状态的持久化,我们需要在Downloader类中增加状态检查和存储逻辑。

package com.example.downloadmanager;

import java.io.*;

// ... existing Downloader class ...

public class Downloader {
    // ... existing fields ...

    private File downloadStateFile;

    public Downloader(String fileUrl, String saveDir) {
        // ... existing code ...
        this.downloadStateFile = new File(saveDir, ".state");
    }

    // 在run方法中检查状态文件
    @Override
    public void run() {
        try {
            if (isCancelled || downloadStateFile.exists()) {
                // 从持久化状态恢复下载或跳过已取消/完成的下载
            }
            // ... existing code ...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 在downloadToFile方法中更新状态文件
    private void downloadToFile(InputStream inputStream) throws IOException {
        // ... existing code ...

        try (ObjectOutputStream stateOut = new ObjectOutputStream(new FileOutputStream(downloadStateFile))) {
            stateOut.writeObject(totalBytesRead);
        }

        // 当下载完成时删除状态文件
        if (isCompleted) {
            if (downloadStateFile.exists()) {
                downloadStateFile.delete();
            }
        }
    }

    // 增加取消下载时的状态保存
    public void cancel() {
        // ... existing code ...
        // 更新状态文件以反映取消状态
    }
}
3. 日志记录

使用java.util.logging包来记录下载过程中的关键信息。

package com.example.downloadmanager;

import java.util.logging.Logger;

public class Downloader implements Runnable {
    // ... existing fields ...
    private static final Logger LOGGER = Logger.getLogger(Downloader.class.getName());

    // ... existing methods ...

    private void downloadFile() {
        // ... existing code ...
        LOGGER.info("Starting download of " + fileUrl);
        // ... more log statements as needed ...
    }
}
4. 配置文件

使用java.util.Properties来管理下载设置。

package com.example.downloadmanager;

import java.io.FileInputStream;
import java.util.Properties;

public class AppConfig {
    private Properties properties;

    public AppConfig(String configFilePath) {
        properties = new Properties();
        try (FileInputStream in = new FileInputStream(configFilePath)) {
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public int getThreadPoolSize() {
        return Integer.parseInt(properties.getProperty("threadPoolSize", "5"));
    }

    public int getMaxDownloadSpeed() {
        return Integer.parseInt(properties.getProperty("maxDownloadSpeed", "1024"));
    }

    // ... more configuration properties as needed ...
}

然后在Main类或DownloadUI中使用AppConfig来初始化DownloadManager

5. 测试和验证

运行应用程序,使用用户界面启动下载任务,并观察程序的行为是否符合预期。

6. 总结

通过增加用户界面、持久化下载状态、日志记录和配置文件管理,我们的多线程文件下载管理器变得更加用户友好和灵活。这些特性为用户提供了更好的交互体验,并提高了应用程序的健壮性和可配置性。

这个案例展示了Java多线程编程在实际应用中的广泛应用,以及如何使用Java的标准库来构建具有复杂功能的应用程序。在实际开发中,您可能还需要考虑更多的异常处理、安全性和性能优化。

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值