gobispo
gobispo

Reputation: 91

JFrame calling multiple concurrent Threads that updates a JProgressBar in the caller JFrame

This is how the thing goes: I have a JFrame for the main window of a Java application, that contains a panel with multiple JProgressBar. I want to start a Thread for each JProgressBar that will start another Threads on their own. When any of this "secondary" threads finishes, I want to update the JProgressBar in my JFrame. Moreover, before arranging all this, as I don't want the user to be able to click anything on the JFrame, I also want to setEnabled(false) some buttons in the JFrame. Easy?

ActivarBotones abFalse = new ActivarBotones(false);
abFalse.start();
try {
    abFalse.join();
} catch (InterruptedException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

EstablecerConexiones ec = new EstablecerConexiones();
ec.start();
try {
    ec.join();
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

ActivarBotones abTrue = new ActivarBotones(true);
abTrue.start();

Two problems.

  1. If I run the code from above, nothing got updated. If I only start the ec thread, everything works almost right.

  2. I don't know much about synchronization and don't know what should I do to start concurrently all the "primary" threads.

Any clues?

Upvotes: 4

Views: 910

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347314

Here's a little sample, I don't think it answers all your questions, but it demonstrates the basic concept (have a sick wife & taking care of a 6 month old, typing with one hand :P)

public class ThreadedProgress {

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

    public ThreadedProgress() {
        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) {
                }

                JPanel progressPane = new JPanel(new GridBagLayout());
                JProgressBar progressBar = new JProgressBar(0, 100);
                progressPane.add(progressBar);

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

                new Thread(new MainThread(progressBar)).start();

            }

        });
    }

    public interface CallBack {
        public void done(Runnable runnable);
    }

    public static class MainThread implements CallBack, Runnable {

        public static final Object UPDATE_LOCK = new Object();
        public static final Object WAIT_LOCK = new Object();
        private List<Runnable> running = new ArrayList<>(25);
        private List<Runnable> completed = new ArrayList<>(25);
        private final JProgressBar progressBar;

        public MainThread(JProgressBar progressBar) {
            this.progressBar = progressBar;
        }

        @Override
        public void done(Runnable runnable) {
            synchronized (UPDATE_LOCK) {
                running.remove(runnable);
                completed.add(runnable);
            }
            int count = running.size() + completed.size();
            updateProgress(completed.size(), count);
            synchronized (WAIT_LOCK) {
                WAIT_LOCK.notify();
            }
        }

        protected void updateProgress(final int value, final int count) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    int progress = Math.round(((float) value / (float) count) * 100f);
                    progressBar.setValue(progress);
                }
            });
        }

        @Override
        public void run() {
            for (int index = 0; index < 5; index++) {
                ChildSpawn spawn = new ChildSpawn(this);
                running.add(spawn);
            }
            for (Runnable runnable : running) {
                new Thread(runnable).start();
            }

            while (running.size() > 0) {
                synchronized (WAIT_LOCK) {
                    try {
                        WAIT_LOCK.wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
            System.out.println("I'm all done");
        }
    }

    public static class ChildSpawn implements Runnable {

        private CallBack callBack;

        public ChildSpawn(CallBack callBack) {
            this.callBack = callBack;
        }

        @Override
        public void run() {
            try {
                Thread.sleep((long)Math.round(Math.random() * 5000));
            } catch (InterruptedException ex) {
            }

            callBack.done(this);            
        }

    }

}

Upvotes: 2

Erick Robertson
Erick Robertson

Reputation: 33082

The ActivarBotones thread is not completing.

If it works when you don't run that thread, then there's something in that thread that's not completing. Otherwise, it would be getting through to the EstablecerConexiones thread. Since you call .join() on this thread after starting it, the code won't proceed until that thread is complete. So there must be something in there that's blocking or caught in a loop.

Run your application in debug mode and put a breakpoint in the ActivarBotones thread. Trace through it and see why it's not finishing.

For your second problem, if you start each main thread but do not join them until they are all started, they will all run concurrently. Of course, this is oversimplifying things greatly. Many people prefer to use executor services to control their threads. You also have to worry about thread-safe implementation so you don't run into synchronization issues. Finally, if you're interacting with Swing components, then all of that needs to be done on the dedicated Event Dispatch Thread.

Upvotes: 1

Related Questions