0

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

Redoman
  • 3,059
  • 3
  • 34
  • 62
  • http://stackoverflow.com/questions/1252190/how-to-wait-for-a-set-of-threads-to-complete – nikis Apr 30 '14 at 13:01
  • @kleopatra please to see woodoo Win8 / JDK1.7_021 / Java051 without invokeLater – mKorbel Apr 30 '14 at 13:13
  • @mKorbel no idea what you mean ;-) – kleopatra Apr 30 '14 at 13:16
  • @jj_ she ( kleopatra) meaning that `progBar.setValue(progBar.getValue()+1);` should be wrapped inside `invokeLater()` – mKorbel Apr 30 '14 at 13:17
  • @kleopatra see my post here, not working (JProgressBar freeze) in Java versions lower than 1.7_051 – mKorbel Apr 30 '14 at 13:18
  • 1
    @mKorbel kleopatra Thanks, that is right, correcting that part of the code. – Redoman Apr 30 '14 at 13:20
  • @mKorbel you got my downvote for such a convoluted (though maybe technically correct, didn't dig into it) way of circumventing the EDT rule, anyway ;-) – kleopatra Apr 30 '14 at 13:20
  • @kleopatra doesn't matter :-) (`you got my downvote for such ...`) but intererting bug in current JRE isn't it :-), rest about EDT rulles is in code my comment bellow `model.setValueAt(...` – mKorbel Apr 30 '14 at 13:24
  • 1
    @mKorbel: why is it a bug if some rules you have invented do not work? Modifying data models from different threads used by Swing from the EDT at the same time **never** was allowed. So if you found a jre version where it happens to work it’s just working by accident but not a rule. – Holger Apr 30 '14 at 13:27
  • @Holger I'm tested by running in another JREs but without luck, code violating EDT rulles, this is standard for Java6, there were Thread Safe methods, model to view interactions is Thread Safe in Java6 (compiled in JDK6, runs in JRE1.6_Xxx) – mKorbel Apr 30 '14 at 13:35
  • 1
    @mKorbel: *where is the official documentation* to your claim? Just because it happened to work does *not* imply that it is thread-safe. It may just imply that the optimizer did a lousy job in that version. Or that you just had a luck. – Holger Apr 30 '14 at 13:40
  • @Holger where is the official documentation == JDK6 SWing APIs, btw all bugs are catched by hardworkers or by luck – mKorbel Apr 30 '14 at 13:42
  • 1
    @mKorbel: you are reversing the logic. You can catch a bug by luck but you can **not** prove the correctness of your code by luck. If the specification does not say it ought to work (and in this case it explicitly states that it is wrong), it is not a bug if it doesn’t work. – Holger Apr 30 '14 at 13:48
  • 2
    @mKorbel: And the [JDK 6 Swing API is pretty clear (at the end of the page)](http://docs.oracle.com/javase/6/docs/api/javax/swing/package-summary.html): *This restriction also applies to models attached to Swing components. For example, if a TableModel is attached to a JTable, the TableModel should only be modified on the event dispatching thread. If you modify the model on a separate thread you run the risk of exceptions and possible display corruption.* Nothing more to say… – Holger Apr 30 '14 at 13:52
  • @Holger this isn't true, really empty discusion, (right this note contains all Java version in Swing APis), EDT violations in Java7 v.s Java6 we are solving here last two years for FileIO/JDBC and JTable/JTextComponets, funny issue was reason to remove invokeLater in my post, and moving to the commented part of code, whats wrong with, coin has always both sides (my coins has always both sides) ... – mKorbel Apr 30 '14 at 22:40

3 Answers3

2

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();
Holger
  • 285,553
  • 42
  • 434
  • 765
  • @mKorbel: why shall I insert a break in an ordinary `for` loop with an ordinary end condition? – Holger Apr 30 '14 at 13:24
  • theoretically JProgressBar is by default from zero to 100 (API) shure there isn't difference value at 1mio should be painted as 100, but OP has question about `How can I know where all the threads are finished?` – mKorbel Apr 30 '14 at 13:29
  • 1
    This code does not care about the `JProgressBar`’s range. It uses the *same* variable for starting `times` number of threads and for waiting to reach *old progress value* `+times`. – Holger Apr 30 '14 at 13:32
  • @Holger the thing is that by putting ```progBar.setValue(progBar.getValue()+1)``` inside invokeLater the increment will be fired (and drawn) while the actual operation ```(Thread.sleep(5000))``` is still running, instead I wanted it to be incremented when each thread finished. That's why before I had ```progBar.setValue(progBar.getValue()+1)``` outside of invokeLater (which indeed is wrong). Any ideas on how to do it? – Redoman Apr 30 '14 at 14:08
  • 1
    @jj_: Since the `invokeLater` is placed *after* the `sleep`, it is executed *after* the sleep as intended. There may be *other* threads in their *sleep* call but the particular thread executing `invokeLater` surely has completed its `sleep` operation. That ordering within a thread does not change (not even from other threads perspective as `invokeLater` is a thread-safe operation). – Holger Apr 30 '14 at 14:13
  • @Holger Thanks! Thanks for the patience to write such a basic explaination! I usually know how threads works.., but as I am new to java and I am chewing a lot of code in little time I am falling for such crazy things. – Redoman Apr 30 '14 at 14:29
  • So at the end of the day is it ok if I launch threads with Thread.new and from that thread I call invokeLater to update the GUI? I am not doing anything wrong "Swing-wise" right? – Redoman Apr 30 '14 at 14:31
  • 1
    That’s right. Sometimes the key point is to be aware of what does update the UI (as it might be hidden in another method you invoke), but the principle is clear, do background things in the background thread and update the UI afterwards using `invokeLater`. Other tools like `ExecutorService` or `SwingWorker` are just tools to make it *easier* to implement such things, but the logic stays the same. – Holger Apr 30 '14 at 14:55
  • Adding a link to [Multi-threading in Java Swing with SwingWorker ](http://java.dzone.com/articles/multi-threading-java-swing) for reference as it seems to perfectly explain this all in a nice and easy way. – Redoman Apr 30 '14 at 15:36
  • @Holger one little doubt here still: update the UI in EDT only, ok, but passing around UI components in other threads, to read their states, values and so on... is all ok? Right? – Redoman May 01 '14 at 07:20
  • 1
    @jj_: reading the component’s state from a different thread while updates are ongoing in the EDT will cause problems as well. There are only a few exceptions, i.e. reading a simple property (not calculated from multiple properties) represented by an immutable value type (e.g. `String`) might work. Reading properties that are definitely not updated while you read them (which are under your control) would be ok too. – Holger May 02 '14 at 07:41
  • @jj_: But in general components should be accessed from the EDT only. You may pass the *reference* to the background thread so it can pass it to the code executed via `invokeLater` then but the background thread should not invoke method on the components. Note that even getter methods might have internal side-effects. Data that the background thread really processed should be either, not shared with the EDT (i.e. copies) or immutable. – Holger May 02 '14 at 07:45
  • @Holger great answer thanks. Last thing that is puzzling my mind: what about SwingWorkers. You should start them on EDT or in background threads? – Redoman May 02 '14 at 09:13
  • Well, [its documented](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html) (look at the “Workflow” section). It might be started by any thread but usually you stuff them with data from the UI before starting them so you start them from the EDT. But it’s possible that a background task starts another worker… – Holger May 02 '14 at 09:42
  • Thanks, I didn't notice that part in documentation! My doubts were arising from the fact that SwingWorkers done() method is purposedly made to work safely in EDT (that's the whole point in using a SwingWorker), so that's why it seemed a bit odd to start a SwingWorker from a EDT... – Redoman May 08 '14 at 10:58
1

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.

Tim B
  • 40,716
  • 16
  • 83
  • 128
  • The bit of code in my question is inside a a createAndShowGUI method which is called by an "outer" javax.swing.SwingUtilities.invokeLater like this: ```public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() { createAndShowGUI(); } }); }``` so I'm already in the EDT. – Redoman Apr 30 '14 at 12:45
  • The contents of run() are happening on your new Thread called T, not in the EDT – Tim B Apr 30 '14 at 13:04
  • This is exactly what I thought, but if I omit the invokeLater call, the code blocks on each sleep, while if I use invokeLater below (like I did) it does not! Please check the link I provided, it's done in the same way (but it's just one thread). – Redoman Apr 30 '14 at 13:08
  • It seems like you have some fundamental holes in your understanding of threading but I don't really have time to go through them now. Take a look at `SwingWorker` and just use that to do the work, it will handle all this for you. – Tim B Apr 30 '14 at 13:10
  • You were right! I just totally confused myself by thinking too much :) Corrected the code. Will look into SwingWorker! – Redoman Apr 30 '14 at 14:40
-2

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();
            }
        });    
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • "not possible because this is endless loop without break;" It is not an endless loop! Why you say so? – Redoman Apr 30 '14 at 13:28
  • 2
    @mKorbel: [Swing’s threading policy:](http://docs.oracle.com/javase/7/docs/api/javax/swing/package-summary.html#threading) **All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread.** So where is it documented that you can modify a table model from a different thread? – Holger Apr 30 '14 at 13:30
  • @jj_ DYM `for(int x = 0; x < 20; x = x+1) {` – mKorbel Apr 30 '14 at 13:30
  • @Holger please to read comments with kleo and my description below setValueAt(...) – mKorbel Apr 30 '14 at 13:32
  • 2
    @mKorbel: your description is not an official documentation. Unless otherwise documented, using that method from another thread *is not allowed*. Even if it happens to run on a particular version, on *your* machine. – Holger Apr 30 '14 at 13:35
  • @Holger `your description is not an official documentation` I tested on WinXP / Win7 / Win8 (various JDK/JRE) before I wrote something here :-), as aside did you read some API for JDK6, there were bunch of Thread Safe methods in Swing APIs, all disappeared in JDK7 – mKorbel Apr 30 '14 at 13:39
  • @mKorbel: “you have tested” is not a documentation. Regarding Java 6 had “bunch of Thread Safe methods in Swing APIs, all disappeared in JDK7”, go ahead and prove, [the documentation is still online](http://docs.oracle.com/javase/6/docs/api/). I don’t see any “thread-safe” note on [`DefaultTableModel.setValueAt`](http://docs.oracle.com/javase/6/docs/api/javax/swing/table/DefaultTableModel.html#setValueAt(java.lang.Object,%20int,%20int))… – Holger Apr 30 '14 at 13:44
  • 1
    @mKorbel until x is less than 20 keep adding 1 to x and looping. So at some point x will not be less than 20 anymore and loop will end. What is wrong here? Look at this? [jdoodle.com/a/6n](http://jdoodle.com/a/6n) – Redoman Apr 30 '14 at 13:44
  • @Holger see my question about [PropertyChangeListener and EDT violated by Thread.sleep(int)](http://stackoverflow.com/questions/8169964/is-mvc-in-swing-thread-safe) – mKorbel Apr 30 '14 at 13:50
  • @Holger EDT returns false, disappeared by default in the case that queue is empty, you have to know very well that there is interesting delay, this expiration period is different between Java6 and Java7 thats, any miracle, nothing else, sry have to going at home (half day of Urlaub) – mKorbel Apr 30 '14 at 13:56
  • 1
    @mKorbel: I don’t see what that link is going to prove. There, again, you are the only one claiming that calling Swing stuff from outside the EDT ought to work. It doesn’t matter whether there are differences in the behavior between Java6 and Java7, you have written broken code which the JREs execute exactly as specified: by exhibiting *undefined behavior*. Fix your code instead of blaming the JRE. Your notorious self-references are no prove. – Holger Apr 30 '14 at 14:02
  • 3
    `DefaultTableModel` uses `Vector`, which is thread-safe, but I'm reluctant to rely on that implementation detail. – trashgod May 03 '14 at 22:10