Question

Inside a createAndShowGUI() method called by javax.swing.SwingUtilities.invokeLater like this...:

public static void main(String[] args) { 
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() { 
            createAndShowGUI(); 
        } 
    }); 
}

...I have the following piece of code which launches multiple threads with invokeLater where each threads increments the value of progBar when it is ran:

int times = 20;

for(int x = 0; x < times; x = x+1) {
        new Thread("T") {
                public void run() {

                        try {                                              
                            Thread.sleep(5000);

                        } catch(InterruptedException ex) {
                            Thread.currentThread().interrupt();
                        }

                        SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                        progBar.setValue(progBar.getValue()+1);
                                }
                        });

                }

        }.start();

}

How can I know where all the threads are finished? If I use a counter inside invokeLater I think I will I run into race conditions.. So what is the right way to do it? Should I use a mutex? Are there some facilities provided by Swing to this purpose? Thanks.

I have implemented the code in this way according to http://www.java2s.com/Code/Java/Threads/InvokeExampleSwingandthread.htm

Was it helpful?

Solution

The Runnables you pass to invokeLater are all executed on the single Event Dispatch Thread. Due to the fact that they are all sent by different threads there is no particular order but they are executed one after another.

So once you have fixed your code by moving the UI updates into the Runnable you are passing to invokeLater you can use Swings notification mechanism to get informed about the finishing of the jobs:

final int end=progBar.getValue() + times;
progBar.addChangeListener(new ChangeListener() {
  public void stateChanged(ChangeEvent e) {
    if(progBar.getValue()==end) {
      System.out.println("all jobs finished");
    }
  }
});
for(int x = 0; x < times; x = x+1) {
  new Thread("T") {
    public void run() {
      try {                                              
          Thread.sleep(5000);
      } catch(InterruptedException ex) {
          Thread.currentThread().interrupt();
      }
      SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                progBar.setValue(progBar.getValue()+1);
              }
      });
    }
  }.start();

OTHER TIPS

InvokeLater actually runs everything that is invoked from the Swing EventDispatchThread - which is probably not what you want at all. They will not run at the same time, they will each run one after the other. What's even worse is that in your example you are making changes to the Swing controls from your thread, but then using InvokeLater afterwards.

This means the counter would actually be thread safe.

What you probably want here is to use a 'SwingWorker' or an 'ExecutorService' and post the results back to the EDT for display.

How can I know where all the threads are finished? If I use a counter inside invokeLater I think I will I run into race conditions..

  • not possible because this is endless loop without break;

So what is the right way to do it?

  • JProgressBar has range from zero to 100 in API, just to test if value in loop is 100 or greather, then to use break;

  • use Runnable#Thread instead of plain vanilla Thread

for example (the same output should be from SwingWorker, but there are is used more than 12 instances, AFAIK note there was a bug about overloading number of..., but for purpose as code for forum is possible to create similair code by using SwingWorker)

enter image description here

enter image description here

enter image description here

import java.awt.Component;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class TableWithProgressBars {

    private static final int maximum = 100;
    private JFrame frame = new JFrame("Progressing");
    Integer[] oneRow = {0, 0, 0, 0};
    private String[] headers = {"One", "Two", "Three", "Four"};
    private Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,};
    private DefaultTableModel model = new DefaultTableModel(data, headers);
    private JTable table = new JTable(model);

    public TableWithProgressBars() {
        table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum));
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Object waiter = new Object();
                synchronized (waiter) {
                    int rows = model.getRowCount();
                    int columns = model.getColumnCount();
                    Random random = new Random(System.currentTimeMillis());
                    boolean done = false;
                    while (!done) {
                        int row = random.nextInt(rows);
                        int column = random.nextInt(columns);
                        Integer value = (Integer) model.getValueAt(row, column);
                        value++;
                        if (value <= maximum) {
                            model.setValueAt(value, row, column); 
                            // model.setValueAt(... must be wrapped into invokeLater()
                            // for production code, otherwise nothing will be repainted
                            // interesting bug or feature in Java7 051, looks like as 
                            // returns some EDT features from Java6 back to Java7 ???
                            try {
                                waiter.wait(15);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        done = true;
                        for (row = 0; row < rows; row++) {
                            for (column = 0; column < columns; column++) {
                                if (!model.getValueAt(row, column).equals(maximum)) {
                                    done = false;
                                    break;
                                }
                            }
                            if (!done) {
                                break;
                            }
                        }
                    }
                    frame.setTitle("All work done");
                }
            }
        }).start();
    }

    private class ProgressRenderer extends JProgressBar implements TableCellRenderer {

        private static final long serialVersionUID = 1L;

        public ProgressRenderer(int min, int max) {
            super(min, max);
            this.setStringPainted(true);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            this.setValue((Integer) value);
            return this;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TableWithProgressBars();
            }
        });    
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top