Java Swing JProgressBar多线程进度条不实时更新问题求助
Java Swing多线程下进度条无法实时更新的问题
我是并行计算领域的新手,正在用Apache NetBeans做Java开发。最近基于Semaphore和Thread实现了一个模拟信用卡交付流程的程序,用5个线程模拟子进程,通过Thread.sleep()模拟每个子进程的耗时。每个子进程对应独立的JProgressBar,总进度条通过信号量控制:每当一个子进程完成,总进度条就增加该子进程对应的百分比。
目前遇到的问题是:BarraGeneral和SubProceso的run方法里模拟了耗时并更新进度条数值,但进度条不会实时显示变化,而是直接从0%跳至100%。控制台打印和调试显示进度条的数值确实在逐次变化,我试过缩短sleep时长,问题依然存在。
相关代码
BarraGeneral类
package espe.edu.ec.laboratorio1p2; import java.util.ArrayList; import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JProgressBar; public class BarraGeneral extends Thread{ private int id; private Semaphore aportes; private ArrayList<Integer> porcentajes; private int nHilos; private JProgressBar bar; BarraGeneral(int id, Semaphore aportes, ArrayList<Integer> porcentajes, int nHilos, JProgressBar bar){ this.id = id; this.aportes = aportes; this.porcentajes = porcentajes; this.nHilos = nHilos; this.bar = bar; } @Override public void run(){ int a; for(int i = 0; i < nHilos; i++){ try { aportes.acquire(); a = bar.getValue(); bar.setValue(a + porcentajes.get(i)); System.out.println(porcentajes.toString()); } catch (InterruptedException ex) { Logger.getLogger(BarraGeneral.class.getName()).log(Level.SEVERE, null, ex); } } } }
SubProceso类
package espe.edu.ec.laboratorio1p2; import java.util.ArrayList; import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JProgressBar; import javax.swing.event.ChangeListener; public class SubProceso extends Thread{ private Semaphore aportes; private int id; private int tiempo; private int porcentaje; private ArrayList<Integer> porcentajes; private JProgressBar bar; SubProceso(Semaphore aportes, int id, int tiempo, int porcentaje, ArrayList<Integer> porcentajes, JProgressBar bar){ this.aportes = aportes; this.id = id; this.tiempo = tiempo; this.porcentaje = porcentaje; this.porcentajes = porcentajes; this.bar = bar; } @Override public void run(){ for(int i = 1; i <= 100; i++){ try{ this.sleep(tiempo); bar.setValue(i); }catch(InterruptedException ex){ Logger.getLogger(SubProceso.class.getName()).log(Level.SEVERE, null, ex); } } System.out.println(porcentaje); porcentajes.add(porcentaje); aportes.release(); } }
Laboratorio1P2类
package espe.edu.ec.laboratorio1p2; import java.util.ArrayList; import java.util.concurrent.Semaphore; import javax.swing.JProgressBar; public class Laboratorio1P2 { public static void correr(JProgressBar barProgresoGeneral, JProgressBar barProgreso1, JProgressBar barProgreso2, JProgressBar barProgreso3, JProgressBar barProgreso4, JProgressBar barProgreso5) { int nHilos = 5; Semaphore aportes = new Semaphore(0); ArrayList<Integer> porcentajes = new ArrayList<Integer>(); ArrayList<SubProceso> subProcesos = new ArrayList<>(); BarraGeneral barraGeneral = new BarraGeneral(1, aportes, porcentajes, nHilos, barProgresoGeneral); subProcesos.add(new SubProceso(aportes, 1, 50, 40, porcentajes, barProgreso1)); subProcesos.add(new SubProceso(aportes, 2, 100, 10, porcentajes, barProgreso2)); subProcesos.add(new SubProceso(aportes, 3, 100, 10, porcentajes, barProgreso3)); subProcesos.add(new SubProceso(aportes, 4, 50, 5, porcentajes, barProgreso4)); subProcesos.add(new SubProceso(aportes, 5, 100, 35, porcentajes, barProgreso5)); barraGeneral.start(); for(int i = 0; i < nHilos; i++){ subProcesos.get(i).start(); } while(barraGeneral.isAlive() || subProcesos.get(0).isAlive() || subProcesos.get(1).isAlive() || subProcesos.get(2).isAlive() || subProcesos.get(3).isAlive() || subProcesos.get(4).isAlive()){ } } }
按钮点击事件
private void iniciarActionPerformed(java.awt.event.ActionEvent evt) { Laboratorio1P2.correr(barProgresoGeneral, barProceso1, barProceso2, barProceso3, barProceso4, barProceso5); }
问题原因
Swing是单线程UI框架,所有组件的创建、更新必须在**事件调度线程(EDT)**中执行。当前代码直接在子线程中调用bar.setValue(),违反了Swing的线程安全规则,导致UI更新请求被积压,直到所有子线程结束后才一次性刷新。另外,correr方法中的while循环在EDT中执行,会阻塞整个UI线程,进一步导致进度条无法实时更新。
解决方案
- 使用
SwingUtilities.invokeLater()将UI更新操作提交到EDT执行 - 移除
correr方法中的while循环,避免阻塞EDT
修改后的代码示例
修正SubProceso类的run方法
@Override public void run(){ for(int i = 1; i <= 100; i++){ try{ this.sleep(tiempo); // 将进度条更新提交到EDT int progress = i; SwingUtilities.invokeLater(() -> bar.setValue(progress)); }catch(InterruptedException ex){ Logger.getLogger(SubProceso.class.getName()).log(Level.SEVERE, null, ex); } } System.out.println(porcentaje); porcentajes.add(porcentaje); aportes.release(); }
修正BarraGeneral类的run方法
@Override public void run(){ int a; for(int i = 0; i < nHilos; i++){ try { aportes.acquire(); a = bar.getValue(); int newProgress = a + porcentajes.get(i); // 将总进度条更新提交到EDT SwingUtilities.invokeLater(() -> bar.setValue(newProgress)); System.out.println(porcentajes.toString()); } catch (InterruptedException ex) { Logger.getLogger(BarraGeneral.class.getName()).log(Level.SEVERE, null, ex); } } }
修正Laboratorio1P2类的correr方法
移除阻塞EDT的while循环:
public static void correr(JProgressBar barProgresoGeneral, JProgressBar barProgreso1, JProgressBar barProgreso2, JProgressBar barProgreso3, JProgressBar barProgreso4, JProgressBar barProgreso5) { int nHilos = 5; Semaphore aportes = new Semaphore(0); ArrayList<Integer> porcentajes = new ArrayList<Integer>(); ArrayList<SubProceso> subProcesos = new ArrayList<>(); BarraGeneral barraGeneral = new BarraGeneral(1, aportes, porcentajes, nHilos, barProgresoGeneral); subProcesos.add(new SubProceso(aportes, 1, 50, 40, porcentajes, barProgreso1)); subProcesos.add(new SubProceso(aportes, 2, 100, 10, porcentajes, barProgreso2)); subProcesos.add(new SubProceso(aportes, 3, 100, 10, porcentajes, barProgreso3)); subProcesos.add(new SubProceso(aportes, 4, 50, 5, porcentajes, barProgreso4)); subProcesos.add(new SubProceso(aportes, 5, 100, 35, porcentajes, barProgreso5)); barraGeneral.start(); for(int i = 0; i < nHilos; i++){ subProcesos.get(i).start(); } // 移除阻塞EDT的while循环 }
内容的提问来源于stack exchange,提问作者ruper
相关产品推荐
相关产品推荐

