1

I'm afraid that this one is a little tricky since I couldn't recreate the issue in the example I wrote for this question (the example below works perfectly). Hopefully someone may have a clue as to possible problems with the actual application. I wrote a app with performs several long text operations. Each operation is done in its own thread. There is a Frame that is updated by the threads to let a user see how everything is progressing.

The problem is that the frame is displayed with all the updates that were sent to it only after all the threads are done with their jobs.
I've simplified the entire app into the code below but as I said the problem is that it works here. Any ideas are very welcome.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Main {

    private MyFrame frame;
    private ExecutorService executorService;

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.startProcess();
    }

    public void startProcess() throws InterruptedException {
        // Initialize the frame
        frame = new MyFrame();
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                frame.setVisible(true);
            }
        });

        // Initialize executorService for 3 threads and also 6 runnables
        executorService = Executors.newFixedThreadPool(3);
        MyRunnable runnable;
        for(int i = 0; i < 6; i++) {
            runnable = new MyRunnable(this, i);
            executorService.execute(runnable);
        }

        // Start runnables
        executorService.shutdown();

        // Wait until all runnables are executed
        while (!executorService.isTerminated()) {
            Thread.sleep(10000);
        }

        // When all runnables are done close the frame
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                frame.setVisible(false);
            }
        });
    }

    // Update the frame display
    public synchronized void updateDisplay(final String update) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                frame.updateDisplay(update);
            }
        });
    }

    private class MyFrame extends JFrame {
        private JPanel contentPane;
        private JLabel lblDisplay;

        public MyFrame() {
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            contentPane = new JPanel();
            contentPane.setLayout(new BorderLayout(0, 0));
            setContentPane(contentPane);

            lblDisplay = new JLabel("Display");
            contentPane.add(lblDisplay, BorderLayout.CENTER);

            pack();
        }

        public void updateDisplay(String update) {
            lblDisplay.setText(update);
            pack();
        }
    }

    private class MyRunnable implements Runnable {
        private int id;
        private Main main;

        public MyRunnable (Main main, int id) {
            this.main = main;
            this.id = id;
        }

        @Override
        public void run() {
            for(int i = 0; i < 3; i++) {
                main.updateDisplay("Runnable " + id + " stepped " + i + " times.");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
yizzlez
  • 8,757
  • 4
  • 29
  • 44
Yoav
  • 2,077
  • 6
  • 27
  • 48

1 Answers1

3

that job for SwingWorker and only one JFrame, example here

output from Runnable#Thread to the GUI must be wrapped into invokeLater() example here

or invoke SwingWorker from Executor

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 1. Do you mean that I should be using a SwingWorker for the tasks instead of runnables? 2. Do yo mean that the invikeLater() warp should be in the runnables instead of in the main class? – Yoav Nov 07 '11 at 16:25
  • you have issue with http://download.oracle.com/javase/tutorial/uiswing/concurrency/index.html, and create a new JFrame for new process started from Execuror, see edit – mKorbel Nov 07 '11 at 16:41
  • you have to decide, `1)` start Runnable from Executor, `2)` start SwingWorker from Executor, `3)` start Runnable without Executor, `4)` start SwingWorker without Executor, all option are correct, you have to decide, up to you, then you have to clean-up code – mKorbel Nov 07 '11 at 16:45
  • +1 For `SwingWorker`; also consider `CountDownLatch` in `SwingWorker`, mentioned [here](http://stackoverflow.com/questions/1906670/how-to-make-the-main-thread-wait-for-the-other-threads-to-complete-in-threadpool/1909622#1909622). – trashgod Nov 07 '11 at 22:48