Skip to content

Java GUI – Progress bar doesn’t update until the async task is finished

I am using CompletableFuture to run a long running operation. Meanwhile, i use SwingWorker to update a progress bar with increments of 5.

            JProgressBar progressBar = new JProgressBar();
            progressBar.setMinimum(0);
            progressBar.setMaximum(100);
            progressBar.setValue(0);
            progressBar.setStringPainted(true);
            CompletableFuture<NetworkToolsTableModel> completableFuture = CompletableFuture.supplyAsync(() -> asyncTask());
            
            SwingWorker worker = new SwingWorker() {
                @Override
                protected Object doInBackground() throws Exception {
                    int val = 0;
                    while (!completableFuture.isDone()) {
                        if (val < 100) {
                            val += 5;
                            progressBar.setValue(val);
                        }
                        Rectangle bounds = progressBar.getBounds(null);
                        progressBar.paintImmediately(bounds);
                    }
                    return null;
                }
            };
            worker.execute();

The progress bar doesn’t update until the asynchronous method is finished. I have also tried doing this operation on the EDT thread, but to no avail. What you are seeing is basically me trying to just do trial and error at this point.

Why is the progress bar not updating? How can I fix this?

Answer

Stop and take a closer look at Worker Threads and SwingWorker.

A SwingWorker is meant to allow you to perform a long running task, which may produce intermediate results, and allow you to publish those results back to the Event Dispatching Thread to be safely processed (via the process method).

You “could” publish the progress updates and update the progress bar in process method, but SwingWorker already provides a progress property, which you can monitor. Take a look at the SwingWorker JavaDocs for a number of ready made examples!

Run Example

Simple Example

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                ProgressPane progressPane = new ProgressPane();
                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(progressPane);
                frame.setSize(200, 200);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

//                progressPane.doWork();
            }
        });
    }

    public class ProgressPane extends JPanel {

        private JProgressBar progressBar;
        private JButton startButton;

        public ProgressPane() {

            setLayout(new GridBagLayout());
            progressBar = new JProgressBar();

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(progressBar, gbc);
            startButton = new JButton("Start");
            gbc.gridy = 1;
            add(startButton, gbc);

            startButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    startButton.setEnabled(false);
                    doWork();
                }
            });

        }

        public void doWork() {

            Worker worker = new Worker();
            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("progress".equals(evt.getPropertyName())) {
                        progressBar.setValue((Integer) evt.getNewValue());
                    }
                }
            });

            worker.execute();

        }

        public class Worker extends SwingWorker<Object, Object> {

            @Override
            protected void done() {
                startButton.setEnabled(true);
            }

            @Override
            protected Object doInBackground() throws Exception {

                for (int index = 0; index < 1000; index++) {
                    int progress = Math.round(((float) index / 1000f) * 100f);
                    setProgress(progress);

                    Thread.sleep(10);
                }

                return null;
            }
        }
    }
}