I'm making a simple game in Java using swing and am having problems with my GUI freezing up sporadically (due to threading issues most probably) after a button press that is supposed to trigger a switch in JPanels.
I posted a related thread here, which has more details about the actual code I'm currently using (although I did update the countdown and get that working fine). From answers to that thread, it seems like usingSwingUtilities.invokeLater() or invokeAndWait() might be what I need to solve the problem, but I'm not sure where in my code it is necessary or exactly how to implement it.
I don't know that much about threading and could use any help (preferably somewhat detailed and with some sample code) that I can get. Let me know if any further details would be useful.
See: Tutorial: Concurrency in Swing
Generally speaking, the Event Dispatch Thread is a single thread, chugging through the event queue, processing one at a time.
SwingUtilities.invokeLater(..)
puts a Runnable on this queue. So it will be processed by the EDT when the EDT finishes everything on the queue before it (This is why sleeping on the queue blocks other events like repainting). It's relatively unusual to call invokeLater(..) from the EDT itself, though there are situations where it is useful (usually as a hack). I don't think I have had a legitimate use for SwingUtilities.invokeAndWait(..) in the last 6 years. Maybe once.
javax.swing.Timer can be configured to fire once or periodically. When it fires, it puts an event on the EDT queue. If you have computationally-intensive processing that need to be done, consider using javax.swing.SwingWorker to do the computation on another thread, and give you back the result in a thread-safe manner (this is also comparatively rare).
A good point to look is the docs. In your case, this explains how SwingUtilities.invokeLater() works and where to use it:
Causes doRun.run() to be executed asynchronously on the AWT event
dispatching thread. This method should be used when an application
thread needs to update the GUI.
So, in your actions that modifies the GUI you must use the invokeLater method to assure that the GUI wont freeze.
Another good resource is the Java tutorials. They cover concurrency in Swing.
I have create a WorkerThread class which take care of Threads and GUI current/main thread . i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the XXXServer ...");
// initializing the Socket Server
try {
XXXServer xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}
In order to invoke an action in the existing WorkerThread, one would intuitively send a user defined event using SwingUtilities.invokeLater() to a JFrame's actionPerformed() method as
class TestFrame extends JFrame implements ActionListener
{
...
private class Performer implements Runnable
{
ActionEvent event;
Performer(ActionEvent event)
{
this.event = event;
}
#Override
public void run()
{
actionPerformed(event);
}
}
synchronized protected void invokeLater(ActionEvent event)
{
SwingUtilities.invokeLater(new Performer(event));
}
public void actionPerformed(ActionEvent event)
{
...
}
}
Now, TestFrame.invokeLater() called in any Thread will be processed in TestFrame.actionPerformed() in existing WorkerThread .
Related
Related to my previous question: Call repaint from another class in Java?
I'm new to Java and I've had a look at some tutorials on SwingWorker. Yet, I'm unsure how to implement it with the example code I gave in the previous question.
Can anyone please explain how to use SwingWorker with regards to my code snippet and/or point me towards a decent tutorial? I have looked but I'm not sure I understand yet.
Generally, SwingWorker is used to perform long-running tasks in Swing.
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up, so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive by which prioritizing the other AWT events before running the desired task (in the form of a Runnable).
However, the problem with SwingUtilities is that it didn't allow returning data from the the executed Runnable to the original method. This is what SwingWorker was designed to address.
The Java Tutorial has a section on SwingWorker.
Here's an example where a SwingWorker is used to execute a time-consuming task on a separate thread, and displays a message box a second later with the answer.
First off, a class extending SwingWorker will be made:
class AnswerWorker extends SwingWorker<Integer, Integer>
{
protected Integer doInBackground() throws Exception
{
// Do a time-consuming task.
Thread.sleep(1000);
return 42;
}
protected void done()
{
try
{
JOptionPane.showMessageDialog(f, get());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The return type of the doInBackground and get methods are specified as the first type of the SwingWorker, and the second type is the type used to return for the publish and process methods, which are not used in this example.
Then, in order to invoke the SwingWorker, the execute method is called. In this example, we'll hook an ActionListener to a JButton to execute the AnswerWorker:
JButton b = new JButton("Answer!");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
new AnswerWorker().execute();
}
});
The above button can be added to a JFrame, and clicked on to get a message box a second later. The following can be used to initialize the GUI for a Swing application:
private void makeGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new FlowLayout());
// include: "class AnswerWorker" code here.
// include: "JButton" b code here.
f.getContentPane().add(b);
f.getContentPane().add(new JButton("Nothing"));
f.pack();
f.setVisible(true);
}
Once the application is run, there will be two buttons. One labeled "Answer!" and another "Nothing". When one clicks on the "Answer!" button, nothing will happen at first, but clicking on the "Nothing" button will work and demonstrate that the GUI is responsive.
And, one second later, the result of the AnswerWorker will appear in the message box.
Agree:
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up.
Do not agree:
so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive..
invokeLater still runs the code on the EDT, and can freeze your UI!! Try this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
At least I, cannot move my mouse once I click the button which triggers the actionPerformed with the above code. Am I missing something?
Hey i just need a question answered...
How would i make the following code not freeze my whole JFrame?
try {
Thread.sleep(Integer.parseInt(delayField.getText()) * 1000);
System.out.println("Hello!");
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
use a different thread to perform this task. If you do this in the main UI thread then it will freeze.. For example you can do following
new Thread() {
#Override
public void run() {
try {
Thread.sleep(Integer.parseInt(delayField.getText()) * 1000);
System.out.println("Hello!");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}.start();
UPDATE
AFter wise suggestions of Robin and Marko I am updating the answer with a better solution.
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Hello!");
}
};
javax.swing.Timer t = new javax.swing.Timer(Integer.parseInt(delayField.getText()) * 1000, taskPerformer);
t.setRepeats(false);
t.start();
Whenever you are about to use Thread.sleep in your GUI code, stop yourself and think of Swing Timer, which is the right tool for the job. Schedule the task you need to perform with a delay.
Using another thread for this is not the best advice: it wastes a heavy system resource (a thread) to do absolutely nothing but wait.
This is not the correct way to use threads in java . You should use swingutilities.invokelater
swing utils invoke later
You don't want to execute this on the UI (or event dispatch thread) thread. Rather in a separate thread. Otherwise (as you've seen) you'll block the UI.
It's a good practice to perform time-consuming operations on a separate thread, and make use of SwingUtilities.invokeLater() if those threads need to perform some subsequent UI action (e.g. in the above display "Hello" in the UI)
Hey i just need a question answered...
How would i make the following code not freeze my whole JFrame?
try {
Thread.sleep(Integer.parseInt(delayField.getText()) * 1000);
System.out.println("Hello!");
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
use a different thread to perform this task. If you do this in the main UI thread then it will freeze.. For example you can do following
new Thread() {
#Override
public void run() {
try {
Thread.sleep(Integer.parseInt(delayField.getText()) * 1000);
System.out.println("Hello!");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}.start();
UPDATE
AFter wise suggestions of Robin and Marko I am updating the answer with a better solution.
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Hello!");
}
};
javax.swing.Timer t = new javax.swing.Timer(Integer.parseInt(delayField.getText()) * 1000, taskPerformer);
t.setRepeats(false);
t.start();
Whenever you are about to use Thread.sleep in your GUI code, stop yourself and think of Swing Timer, which is the right tool for the job. Schedule the task you need to perform with a delay.
Using another thread for this is not the best advice: it wastes a heavy system resource (a thread) to do absolutely nothing but wait.
This is not the correct way to use threads in java . You should use swingutilities.invokelater
swing utils invoke later
You don't want to execute this on the UI (or event dispatch thread) thread. Rather in a separate thread. Otherwise (as you've seen) you'll block the UI.
It's a good practice to perform time-consuming operations on a separate thread, and make use of SwingUtilities.invokeLater() if those threads need to perform some subsequent UI action (e.g. in the above display "Hello" in the UI)
I am relatively new to java and was curious about how ActionListeners work. Say I have an action listener for a timer implemented as follows:
class TimerActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
//perform some operation
}
}
What will happen if the timer is set to run faster than the code in my actionlistener class can execute. Does the code finish executing and ignore new requests until it is done (like an interrupt). Or does the new call to actionlistener take priority over the current instance - such that the code will never complete?
The timer's timing is done in thread distinct from the event dispatch thread (or EDT) which is the thread that runs the code in the ActionListener. So even if the actionPerformed code is slow, the timer will keep on firing regardless and will queue its actionPerformed code on the event queue which will likely get backed up and the event thread will get clogged and the application will be unresponsive or poorly responsive.
A take-home point is to avoid calling any code that takes a bit of time on the event thread as it will make the GUI unresponsive. Consider using a SwingWorker for cases like this.
Edit: Please see trashgod's comment below for the win!
Based on the posts from hovercraft and trashgod, it seems that the Timer events do not queue by their default setting. (i.e. new timer events will be ignored until the timer event handler code has finished executing.)
You can test it yourself implementing something as follows:
class TimerActionListener implements ActionListener {
public static int inst = 1;
public void actionPerformed(ActionEvent e) {
int id = inst++;
System.out.println("Executing instance: " + id);
try { Thread.sleep(3000); } catch (Exception e) {} //For sleep 3 seconds
System.out.println("Instance: " + id + "done! ");
}
}
So my program has multiple classes and after one of them has run, it'd like it so it appends the text area in the main class GUI with a 'finished' message
ta.append("Search Complete\n");
and this is the code that needs to complete
statCl.addActionListener(new ActionListener(){
public void actionPerformed (ActionEvent e) {
try {
ta.append("Searching...\n");
task.execute();
} catch (Exception IOE) {}
}
});
So it is in the class where task where actual code runs.
Any advice or help would be amazing, thanks.
If the task.execute() method doesn't start launch an operation in another thread, then the GUI will be freezed, and nothing will apear in the text area until the operation is finished. So you might just have
ta.append("Searching...\n");
task.execute();
ta.append("Finished");
If the operation is launched in a new thread, then this thread should append in the text area, but it should make sure this append is done in the event dispatch thread (EDT). Your code could thus look like this :
public class Task {
private JTextArea ta;
public Task(JTextArea ta) {
this.ta = ta;
}
public void execute() {
Thread t = new Thread(new Runnable() {
// perform the long operation
// at the end, update the text area, in the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ta.append("finished");
}
});
}
t.start();
}
}
You might also look at SwingWorker, which is designed just for that (and other things like progress update). There is a code example in its class javadoc which does just what you're trying to do.
You should not be performing long-running task on EDT (event dispatching thread):
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
Swing does all it's work on EDT, so you should not block EDT, e.g. run intensive tasks on it. Note: all event handlers are executed on EDT.
So there are two challenges:
Run intensive tasks in a background thread.
Update GUI, which must be done on EDT.
Use SwingWorker to solve this two issues.