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

基于多线程的JProgressBar队列模拟可视化实现问询

Multi-Threaded Queue Simulation with Per-Queue JProgressBars

Hey there! I’ve tackled similar Swing multi-threading UI challenges before, so let’s walk through how to build this properly. The core rule to remember here is that Swing is single-threaded—you can’t touch UI components directly from background threads. We’ll structure the solution to separate queue logic from safe UI updates.

Core Approach

For each queue number entered in your main UI, we’ll spawn two key components:

  • A background thread that handles the actual queue simulation (customer arrival, processing, etc.)
  • A dedicated JFrame with a JProgressBar tied exclusively to that queue’s thread
  • Use SwingUtilities.invokeLater() to safely push UI updates from the background thread to Swing’s Event Dispatch Thread (EDT)

Step-by-Step Implementation

1. Main UI (Queue Input & Launcher)

This is where users enter queue numbers and kick off each simulation:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class QueueSimulatorMain extends JFrame {
    private JTextField queueNumberField;

    public QueueSimulatorMain() {
        setTitle("Queue Simulator Launcher");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        queueNumberField = new JTextField(5);
        JButton launchButton = new JButton("Launch Queue");
        launchButton.addActionListener(this::launchQueue);

        add(new JLabel("Enter Queue Number:"));
        add(queueNumberField);
        add(launchButton);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private void launchQueue(ActionEvent e) {
        try {
            int queueId = Integer.parseInt(queueNumberField.getText().trim());
            // Create progress window for this queue
            QueueProgressWindow progressWindow = new QueueProgressWindow(queueId);
            // Start simulation thread
            new Thread(new QueueSimulatorThread(queueId, progressWindow)).start();
            queueNumberField.setText("");
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(this, "Please enter a valid queue number!", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    public static void main(String[] args) {
        // Always start Swing apps on the EDT
        SwingUtilities.invokeLater(QueueSimulatorMain::new);
    }
}

2. Progress Bar Window (Per Queue)

A lightweight window to hold the JProgressBar for a single queue:

import javax.swing.*;
import java.awt.*;

public class QueueProgressWindow extends JFrame {
    private JProgressBar progressBar;

    public QueueProgressWindow(int queueId) {
        setTitle("Queue " + queueId + " Status");
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setLayout(new BorderLayout());

        progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true); // Show percentage text
        progressBar.setString("Waiting for customers...");

        add(new JLabel("Queue " + queueId + " Customer Flow:"), BorderLayout.NORTH);
        add(progressBar, BorderLayout.CENTER);

        pack();
        setSize(300, 100);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    // Safe method to update progress bar from background thread
    public void updateProgress(int progress, String status) {
        SwingUtilities.invokeLater(() -> {
            progressBar.setValue(progress);
            progressBar.setString(status);
        });
    }
}

3. Queue Simulation Thread (Background Logic)

This thread handles the actual queue behavior and updates the progress bar:

public class QueueSimulatorThread implements Runnable {
    private final int queueId;
    private final QueueProgressWindow progressWindow;

    public QueueSimulatorThread(int queueId, QueueProgressWindow progressWindow) {
        this.queueId = queueId;
        this.progressWindow = progressWindow;
    }

    @Override
    public void run() {
        try {
            // Simulate customer arrival (fill the queue)
            for (int i = 0; i <= 100; i += 10) {
                progressWindow.updateProgress(i, "Queue filling: " + i + "% full");
                Thread.sleep(500); // Simulate time between arrivals
            }

            // Simulate processing customers (empty the queue)
            for (int i = 100; i >= 0; i -= 10) {
                progressWindow.updateProgress(i, "Processing: " + (100 - i) + "% complete");
                Thread.sleep(700); // Simulate processing time
            }

            progressWindow.updateProgress(0, "Queue empty - waiting for next batch");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            progressWindow.updateProgress(0, "Queue simulation interrupted");
        }
    }
}

Critical Best Practices

  • Never update Swing components from non-EDT threads: Always use SwingUtilities.invokeLater() for UI changes—this prevents weird glitches or crashes.
  • Dispose windows properly: Use DISPOSE_ON_CLOSE for progress windows so closing one doesn’t terminate the entire application.
  • Handle thread interrupts: If you need to stop a queue simulation, make sure to clean up the thread state properly.

Alternative Idea (If Too Many Windows Are Clunky)

If spawning a new window per queue feels messy, you could modify the main UI to use a JTabbedPane:

  • Each tab represents a queue, with its own JProgressBar
  • When a user enters a queue number, add a new tab instead of a new window
  • This keeps all controls in one place while still giving each queue a dedicated progress display

内容的提问来源于stack exchange,提问作者Diana G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:20:13