Java中调用方法时如何等待指定Thread执行完成?多线程下载需求咨询
实现方案
1. 定义封装类统一管理页面状态
不要直接用ArrayList存下载链接,先封装一个类存每个页面对应的下载状态和结果,方便后续状态判断:
public class HtmlPageItem { // 原始下载链接 private final String downloadUrl; // 下载完成后存储的本地文件路径,未下载时为null private String localFilePath; // 下载完成标记 private boolean isDownloaded = false; // 下载失败标记 private boolean isDownloadFailed = false; public HtmlPageItem(String downloadUrl) { this.downloadUrl = downloadUrl; } // 下面的getter、setter方法直接用IDE自动生成即可 public String getDownloadUrl() { return downloadUrl; } public String getLocalFilePath() { return localFilePath; } public void setLocalFilePath(String localFilePath) { this.localFilePath = localFilePath; } public boolean isDownloaded() { return isDownloaded; } public void setDownloaded(boolean downloaded) { isDownloaded = downloaded; } public boolean isDownloadFailed() { return isDownloadFailed; } public void setDownloadFailed(boolean downloadFailed) { isDownloadFailed = downloadFailed; } }
原来的ArrayList<String>替换为List<HtmlPageItem> pageList,初始化时把所有下载链接转为HtmlPageItem对象存入即可。
2. 实现后台下载逻辑
用JDK自带的ExecutorService线程池管理下载任务,不用手动管理线程,逻辑更简单,优先下载第一个页面,下载完成后立刻展示,剩余页面后台并行下载:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.SwingUtilities; import javax.swing.JOptionPane; // 类成员变量,全局只需要初始化一次线程池 private ExecutorService downloadPool = Executors.newFixedThreadPool(3); // 3个线程并行下载,可自行调整数量 // 记录当前展示的页面下标 private int currentPageIndex = 0; // 页面初始化时调用该方法启动下载 private void startAllDownload(List<HtmlPageItem> pageList) { if (pageList == null || pageList.isEmpty()) return; // 优先提交第一个页面的下载任务 HtmlPageItem firstPage = pageList.get(0); downloadPool.submit(() -> { // 执行下载,替换为你自己的下载逻辑 boolean downloadSuccess = doDownload(firstPage); if (downloadSuccess) { // JxBrowser的UI操作必须切到主线程执行,如果你用JavaFX就把SwingUtilities换成Platform.runLater SwingUtilities.invokeLater(() -> { // 加载第一个页面的本地文件 browser.loadURL("file://" + firstPage.getLocalFilePath()); }); } // 第一个页面下载完成后,提交剩余所有页面的下载任务 for (int i = 1; i < pageList.size(); i++) { HtmlPageItem item = pageList.get(i); downloadPool.submit(() -> doDownload(item)); } }); } // 下载文件的具体逻辑,替换为你自己的下载代码即可 private boolean doDownload(HtmlPageItem pageItem) { try { String targetUrl = pageItem.getDownloadUrl(); // 你原来的下载逻辑:把targetUrl对应的HTML存到本地,拿到存储路径 String savePath = "你的本地存储目录/" + System.currentTimeMillis() + ".html"; // ......你的原有下载代码...... // 下载成功后更新状态 pageItem.setLocalFilePath(savePath); pageItem.setDownloaded(true); return true; } catch (Exception e) { pageItem.setDownloadFailed(true); e.printStackTrace(); return false; } }
3. 实现next/prev按钮点击逻辑
点击时先判断目标页面的下载状态,未下载完成直接弹提示:
// next按钮点击事件 private void onNextClick() { int nextIndex = currentPageIndex + 1; // 循环切换逻辑,到最后一个就回到第一个 if (nextIndex >= pageList.size()) { nextIndex = 0; } HtmlPageItem targetPage = pageList.get(nextIndex); if (!targetPage.isDownloaded()) { JOptionPane.showMessageDialog(null, "当前页面还在下载中,请稍后再试"); return; } if (targetPage.isDownloadFailed()) { JOptionPane.showMessageDialog(null, "当前页面下载失败"); return; } // 加载目标页面 browser.loadURL("file://" + targetPage.getLocalFilePath()); currentPageIndex = nextIndex; } // prev按钮点击事件 private void onPrevClick() { int prevIndex = currentPageIndex - 1; // 循环切换逻辑,到第一个就回到最后一个 if (prevIndex < 0) { prevIndex = pageList.size() - 1; } HtmlPageItem targetPage = pageList.get(prevIndex); if (!targetPage.isDownloaded()) { JOptionPane.showMessageDialog(null, "当前页面还在下载中,请稍后再试"); return; } if (targetPage.isDownloadFailed()) { JOptionPane.showMessageDialog(null, "当前页面下载失败"); return; } // 加载目标页面 browser.loadURL("file://" + targetPage.getLocalFilePath()); currentPageIndex = prevIndex; }
额外注意
- 程序退出时记得调用
downloadPool.shutdown()关闭线程池,避免资源泄漏 - 如果需要支持下载重试,在失败提示的弹窗里加重试按钮,重新提交对应页面的下载任务即可
内容的提问来源于stack exchange,提问作者iC0d3X
相关产品推荐
相关产品推荐

