在 Java GUI 应用中,如若耗时任务(如网络下载、大量计算)直接运行在主线程中,会导致界面“卡死”或无响应。
解决办法?引入多线程!通过创建独立线程执行任务,主线程只负责更新 UI,既响应流畅,也能处理复杂操作。
📷 主线程与后台线程关系图
less复制编辑[事件分发线程 (EDT)] -- 用户交互与界面更新
|
+--> [Worker 线程] -- 后台任务(下载、计算)
✅ Swing 中一切 UI 更新应当在 EDT 中完成。
java复制编辑// 方式一:继承 Thread 类
class MyThread extends Thread {
public void run() {
System.out.println("执行线程逻辑");
}
}
// 方式二:实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() {
System.out.println("线程任务");
}
}
// 方式三:使用匿名内部类或 Lambda
new Thread(() -> System.out.println("Lambda 线程")).start();
start()
与 run()
的区别方法 | 作用 |
---|---|
run() | 只是普通方法调用,在当前线程执行 |
start() | 创建新线程,并执行 run() |
Swing 提供了 SwingWorker<T, V>
类来管理后台任务执行并在完成后更新 UI。
📷 界面图示:
css复制编辑[ 进度条:■■■■■□□□□□ ] 50%
[ 状态:加载中... ]
java复制编辑import javax.swing.*;
import java.awt.*;
public class SwingWorkerDemo extends JFrame {
private JProgressBar progressBar = new JProgressBar(0, 100);
private JLabel statusLabel = new JLabel("状态:准备中");
public SwingWorkerDemo() {
setTitle("SwingWorker 示例");
setSize(400, 150);
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(progressBar, BorderLayout.CENTER);
add(statusLabel, BorderLayout.SOUTH);
setVisible(true);
startTask();
}
private void startTask() {
SwingWorker<Void, Integer> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i += 10) {
Thread.sleep(500);
publish(i);
}
return null;
}
@Override
protected void process(java.util.List<Integer> chunks) {
int value = chunks.get(chunks.size() - 1);
progressBar.setValue(value);
statusLabel.setText("状态:加载中 (" + value + "%)");
}
@Override
protected void done() {
statusLabel.setText("状态:完成!");
}
};
worker.execute();
}
public static void main(String[] args) {
new SwingWorkerDemo();
}
}
📷 界面结构图:
css复制编辑[输入 URL: _______________] [ 下载 ]
[进度条:■■■□□□□□□ 30% ]
[状态:正在下载文件名.txt ]
java复制编辑class FileDownloader extends SwingWorker<Void, Integer> {
private String url;
private File file;
public FileDownloader(String url, File file) {
this.url = url;
this.file = file;
}
@Override
protected Void doInBackground() throws Exception {
try (InputStream in = new URL(url).openStream();
FileOutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int total = 0, read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
total += read;
publish(total);
}
}
return null;
}
@Override
protected void process(java.util.List<Integer> chunks) {
int latest = chunks.get(chunks.size() - 1);
// 可更新界面进度
}
@Override
protected void done() {
// 提示下载完成
}
}
📌 实际项目中建议添加异常处理与进度百分比显示。
Swing 并不是线程安全的,不能在后台线程直接调用 UI 组件。
java复制编辑SwingUtilities.invokeLater(() -> {
label.setText("更新完成");
});
或者使用 SwingWorker.done()
或 process()
方法,它们本身就在 EDT 中执行。
Swing 中内置 javax.swing.Timer
,可用于定时轮询任务,例如:
📷 图示:每 1 秒更新当前时间
css复制编辑[当前时间:2025-06-10 14:30:12]
java复制编辑Timer timer = new Timer(1000, e -> {
label.setText("当前时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
});
timer.start();
功能:
📷 效果图:
css复制编辑[ 图片1:下载中... 30% ]
[ 图片2:下载完成 √ ]
[ 图片3:下载失败 × ]
🚀 建议使用线程池
ExecutorService
管理多个下载线程。
java复制编辑ExecutorService pool = Executors.newFixedThreadPool(5);
for (String url : urlList) {
pool.submit(() -> downloadImage(url));
}
java复制编辑Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 处理任务
}
});
t.start();
// 中断线程
t.interrupt();
📌
Thread.sleep()
、wait()
等会抛出InterruptedException
,可在此处理中断逻辑。
✅ Java 多线程基础和三种创建方式 ✅ Swing 中使用线程更新 UI 的正确方式 ✅ 使用 SwingWorker 安全管理后台任务 ✅ 实战案例:进度条、下载器、并发处理 ✅ 线程池、定时器、线程中断机制
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。