I see the same effect to Mac OS X. Although your example is correctly synchronized, the platform/JVM variability you see is likely due to vagaries in how threads are scheduled, resulting is starvation. Adding Thread.yield()
to the outer loop in t
mitigates the problem, as shown below. Except for the artificial nature of the example, a hint like Thread.yield()
would not ordinarily be required. In any case, consider SwingWorker
, shown here executing a similarly tight loop for demonstration purposes.
I do not believe that
Thread.yield()
should need to be called in this case at all, despite the artificial nature of the test case, however.
Correct; yielding simply exposes the underlying problem: t
starves the event dispatch thread. Note that the GUI updates promptly in the example below, even without Thread.yield()
. As discussed in this related Q&A, you can try lowering the thread’s priority. Alternatively, run t
in a separate JVM, as suggested here using ProcessBuilder
, which can also run in the background of a SwingWorker
, as shown here.
but why?
All supported platforms are built on single-threaded graphics libraries. It’s fairly easy to block, starve or saturate the governing event dispatch thread. Non-trivial background tasks typically yield implicitly, as when publishing intermediate results, blocking for I/O or waiting on a work queue. A task that does not do may have to yield explicitly.
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MFrame extends JFrame {
private static final int N = 100_000;
private static final String TRY_ME = "Try me!";
private static final String WORKING = "Working…";
public static void main(String[] args) {
EventQueue.invokeLater(new MFrame()::display);
}
private void display() {
JButton tryme = new JButton(TRY_ME);
tryme.addActionListener((e) -> {
Thread t = new Thread(() -> {
int a = 4;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
a *= (i + j);
a += 7;
}
Thread.yield();
}
EventQueue.invokeLater(() -> {
tryme.setText(TRY_ME);
tryme.setEnabled(true);
});
System.out.println("a = " + a);
});
t.start();
tryme.setEnabled(false);
tryme.setText(WORKING);
});
add(tryme);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}