首页 > 编程语言 >Java多线程Demo简介

Java多线程Demo简介

时间:2024-08-07 10:52:55浏览次数:8  
标签:... Java Demo private import new 多线程 public 下载

多线程文件下载管理器

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的标准库来构建具有复杂功能的应用程序。在实际开发中,您可能还需要考虑更多的异常处理、安全性和性能优化。

标签:...,Java,Demo,private,import,new,多线程,public,下载
From: https://blog.csdn.net/hummhumm/article/details/140961244

相关文章

  • Android开发基础04-Java 和 Kotlin
    引言Java和Kotlin是两种主要用于Android开发的编程语言。理解它们的基本概念、特点、优缺点及常见用法,对Android开发者来说非常重要。1.Java基本概念Java是一种面向对象、跨平台的编程语言,于1995年由SunMicrosystems(现为Oracle)发布。它的设计理念是“WriteOnce,Ru......
  • [Java的写法]之MD5对字符串签名操作
    对字符串签名后,1:长字符串变为32位字符:aacfbe08d042fddd8ee778b148efc9232:只要长字符串内容不变,签名后得到的32位字符不变。适合用来做ID等。privateStringgenKeyId(StringkeyStr){returnMd5Utils.getStringMD5(keyStr);}Md5Utils类如下:`importjava.io.File;impor......
  • 计算机毕业设计必看必学!! 87229 基于ssm珠宝店信息管理系统,原创定制程序, java、PHP
    摘要近年来,随着移动互联网的快速发展,电子商务越来越受到网民们的欢迎,电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越赢得网民们的青睐。现今,大量的计算机技术应用于商业领域,......
  • 计算机毕业设计必看必学! ! 79197 基于ssm+mysql的学生心理健康在线咨询平台,原创定制
    摘要:在社会快速发展的影响下,教育业继续发展,大大增加了学生心理健康在线咨询平台的数量、多样性、质量等等的要求,使学生心理健康在线咨询平台的管理和运营比过去十年更加困难。依照这一现实为基础,设计一个快捷而又方便的学生心理健康在线咨询平台是一项十分重要并且有价值的事......
  • 图片增加文本水印(右下角)--Java代码实现
    一.效果展示水印前                                               水印后        二.代码实现 /***在给定的图片上添加文本水印。**@paramsourceImgPath源图片路径*......
  • [JAVA的写法]之List的stream()操作
    List里的遍历pmDesignFiles=pmDesignFiles.stream().map((m)->{m.setLocalPath(“234234”);returnm;}).collect(Collectors.toList());List分组Map<String,List>engMap=proEngList.stream().collect(Collectors.groupingBy(PjDemandBundle::getEngId));Li......
  • JAVA基础:String的常用方法
    目录前言string的常用方法前言上一篇我们学习了string字符串的基本用法,以及string字符串的内部机制,而string也是一个类,他的内部也有很多已经给我们封装好的,方便我们操作字符串的方法,我们是不可能将内部的方法全部记住的,我们只要知道方法是怎么使用的有什么样的效果就行,......
  • 深入解析:23种软件设计模式详解及其分类(创建型、结构型、行为型)附代码示例DEMO
    目录引言一、创建型模式1.简单工厂模式(SimpleFactoryPattern)2.抽象工厂模式(AbstractFactoryPattern)3.单例模式(SingletonPattern)4.建造者模式(BuilderPattern)5.原型模式(PrototypePattern)二、结构型模式1.适配器模式(AdapterPattern)2.桥接模式(BridgePatt......
  • Java中对数组的学习
    数组的概念目录数组的概念声明数组变量创建数组处理数组数组作为函数的参数数组作为函数的返回值数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java语言中提供的数组是用来存储固定大小的同类型元素。你可以声明一个数组变......
  • JavaDS —— 红黑树
    前言还是一样,这里的红黑树重点讲述插入代码的实现,如果对红黑树的删除感兴趣,可以去翻阅其他资料。在数据结构专栏中已经对AVL树的旋转调整做了分析和讲解,这里红黑树也会使用到旋转调整的代码,就不讲述旋转代码的实现,大家如果对旋转不熟悉,可以打开这个文章JavaDS——AVL......