Swing's thread safety rule states that once a Swing component has been realized, we must modify or access that component's state on the event dispatch thread.
A component is considered to be realized if it has been painted or it is ready to be painted.
A top-level container in Swing is realized when we call its pack(), setVisible(true), or show() method for the first time.
When a top-level container is realized, all of its children are also realized.
The event dispatch thread is a thread automatically created by the JVM when it detects that it is working with a Swing application. The JVM uses this thread to execute the Swing component's event handlers.
For example, when we click the JButton, the code in the actionPerformed() method is executed by the event dispatch thread.
The following two classes are helper classes used in a Swing application to deal with its threading model. The classes are
To know if the code is executing in the event dispatch thread, use the static method isEventDispatchThread() of the SwingUtilities class.
It returns true if the code is executing in the event dispatch thread. Otherwise, it returns false.
System.out.println(SwingUtilities.isEventDispatchThread());
We should follow the rules listed below when working with Swing action handler code.
The following code shows the correct way to start a Swing application.
SwingUtilities.invokeLater(() -> { MySwingApp app = new MySwingApp("A Swing App"); app.pack(); app.setVisible(true); });
The SwingUtilities.invokeLater(Runnable r) method will start the event dispatch thread if it is not already started.
The SwingUtilities.invokeLater() method call returns immediately and the run() method of its Runnable argument is executed asynchronously.
There is another important static method called invokeAndWait(Runnable r) in the SwingUtilities class.
This method is executed synchronously and it does not return until the run() method has finished executing on the event dispatch thread. This method may throw an InterruptedException or InvocationTargetException.
The SwingUtilities.invokeAndWait(Runnable r) method should not be called from the event dispatch thread because it will block event dispatch thread.
We can use the invokeAndWait() method of the SwingUtilities class to start a Swing application instead of the invokeLater() method.
try { SwingUtilities.invokeAndWait(() -> { JFrame frame = new JFrame(); frame.pack(); frame.setVisible(true); }); System.out.println("Swing application is running..."); }catch (Exception e) { e.printStackTrace(); }
To perform a time-consuming task in a Swing application, perform long tasks in a separate thread other than the event dispatch thread.
Swing provides a SwingWorker class, which makes it easy to work with multiple threads in a Swing application.
The SwingWorker<T,V> class is declared abstract. The type parameter T is the result type and the type parameter V is the intermediate result type.
We must create your custom class inheriting from it and provide implementation the following methods:
import java.awt.BorderLayout; import java.awt.Container; import java.util.List; //from ww w . ja v a2s .co m import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; class SwingWorkerProcessor extends SwingWorker<Integer, Integer> { private final SwingWorkerFrame frame; private int iteration; private int intervalInMillis; public SwingWorkerProcessor(SwingWorkerFrame frame, int iteration, int intervalInMillis) { this.frame = frame; this.iteration = iteration; if (this.iteration <= 0) { this.iteration = 10; } this.intervalInMillis = intervalInMillis; if (this.intervalInMillis <= 0) { this.intervalInMillis = 1000; } } @Override protected Integer doInBackground() throws Exception { int sum = 0; for (int counter = 1; counter <= iteration; counter++) { sum = sum + counter; this.publish(counter); if (Thread.interrupted()) { throw new InterruptedException(); } if (this.isCancelled()) { break; } Thread.sleep(intervalInMillis); } return sum; } @Override protected void process(List<Integer> data) { for (int counter : data) { frame.updateStatus(counter, iteration); } } @Override public void done() { try { frame.doneProcessing(); } catch (Exception e) { e.printStackTrace(); } } } class SwingWorkerFrame extends JFrame { String startMessage = "Please click the start button..."; JLabel statusLabel = new JLabel(startMessage); JButton startButton = new JButton("Start"); JButton cancelButton = new JButton("Cancel"); SwingWorkerProcessor processor; public SwingWorkerFrame() { this.setDefaultCloseOperation(EXIT_ON_CLOSE); Container contentPane = this.getContentPane(); cancelButton.setEnabled(false); contentPane.add(statusLabel, BorderLayout.NORTH); contentPane.add(startButton, BorderLayout.WEST); contentPane.add(cancelButton, BorderLayout.EAST); startButton.addActionListener(e -> startProcessing()); cancelButton.addActionListener(e -> cancelProcessing()); } public void setButtonStatus(boolean canStart) { if (canStart) { startButton.setEnabled(true); cancelButton.setEnabled(false); } else { startButton.setEnabled(false); cancelButton.setEnabled(true); } } public void startProcessing() { setButtonStatus(false); processor = new SwingWorkerProcessor(this, 10, 1000); processor.execute(); } public void cancelProcessing() { processor.cancel(true); setButtonStatus(true); } public void updateStatus(int counter, int total) { String msg = "Processing " + counter + " of " + total; statusLabel.setText(msg); } public void doneProcessing()throws Exception { if (processor.isCancelled()) { statusLabel.setText("Process cancelled ..."); } else { int sum = processor.get(); statusLabel.setText("Sum is " + sum); setButtonStatus(true); } } } public class Main{ public static void main(String[] args) { SwingUtilities.invokeLater(() -> { SwingWorkerFrame frame = new SwingWorkerFrame(); frame.pack(); frame.setVisible(true); }); } }