You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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线程,进一步导致进度条无法实时更新。

解决方案

  1. 使用SwingUtilities.invokeLater()将UI更新操作提交到EDT执行
  2. 移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:07:02