I'm loading contents of a file to JList component. If I do the loading in main thread, everything seems to be OK - contents are loaded. But when I move loading code to separate thread, select an item in a list and try to reload the list, I get random NullPointer or IndexOutOfBounds exceptions. I'm sure this is some kind of Swing threading issue, but can't determine what.
This is my thread code:
#Override
public void run() {
List<String> textLines = null;
textLines = splitter.split(model.getLedMaxChars(), textLoader.loadText(file));
listener.onTextLoaded(textLines);//listener is in main Swing code
}
Controller is responsible for listening:
#Override
public void onTextLoaded(List<String> textLines) {
view.fileLoaded(model.getCurrentFile());
view.setTextLines(textLines);
view.enableListComponent();
}
And the view updates:
public void setTextLines(List<String> textLines) {
jList.setListData(textLines.toArray());
}
I've tried to leave thread to hang by adding while(true); loop - then everything works OK. If I hit reload without selecting item in a list, everything works too.
Could anyone explain what I'm missing here?
Swing components are usually not thread-safe. This means that only the Swing worker thread should do any modifications:
Runnable worker = new Runnable() {
public void run() {
jList.setListData(textLines.toArray());
}
};
SwingUtilities.invokeLater(worker);
See also:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
Swing is not thread safe, so when you are manipulating GUI elements from other threads many weird things may occur. In your case the simplest solution (but probably not the best) would be to use SwingUtilities.invokeLater
Related
I have something I can't understand: my Swing GUI contains a 'play' and 'pause' button. I have also a static variable that defines 'ON' and 'OFF' states. (The main program generates the GUI).
By cliking on 'play' I change the state of my static variable to 'ON' and I launch a time-consuming process in a thread that also modifies the GUI. As long as the static variable is 'ON' loops in the same process. Clicking on 'pause' would change the static variable to OFF.
But by clicking on 'play' the GUI is freezing and consequently:
The GUI doesn't update
The process can't be 'paused' with my 'pause' button.
I have heard about EDT and SwingWorker but I you have a simple way to do it I take it.
Thank you for your help and forgive my bad english...
The problem is that you're doing the intensive, time-consuming work on the same thread responsible for updating the GUI. SwingWorker allows you to move time-consuming tasks to a separate thread of execution, thereby leaving the UI thread to do its thing uninhibited.
However, it does add a further complication: affinity. Calling methods on UI components generally requires that you do so from the UI thread. Therefore, you need to use special functionality to get back to the UI thread from the worker thread. SwingWorker also gives you this ability.
I suggest you read through this documentation.
You need to read Concurrency in Swing to understand how the EDT and SwingWorkers operate.
All GUI updates are executed on the EDT so when you click a GUI component any method that this calls will be executed on the EDT. If this is a time consuming process then this will block the EDT from executing any futher GUI updates. Hence your GUI is freezing and you can't click the pause button.
You need to use SwingWorker to execute the time consuming process on another thread. The link I provided above details how to do this.
You should not start long-running processes in Swing’s event handler because it will freeze your GUI, you know that now. :) Start it in a new thread. You only need to use a SwingWorker if you’re planning on manipulating the GUI from the worker thread (because Swing is not thread-safe).
This is a pretty straightforward reason: while Java is working on your time-consuming process, it isn't able to update the GUI. Solution: run the time-consuming process in a separate thread. There are a bunch of ways to program that, and it would probably depend somewhat on how your program is written.
The event dispatch thread (EDT) is the only thread in which it's safe to read or update the GUI.
The pause button should be setting the on/off variable in the event dispatch thread.
The time-consuming operation, and the loop, should not be in the EDT. (The loop should also not be running continuously doing nothing but check the variable, or it can easily eat all your CPU. If it has nothing else to do it should check, and then call Thread.sleep() for some length of time (say 100ms).)
If you can prove that the on/off variable is being set to OFF, but that nonetheless it's always read as ON, it may be that the variable's value is not being copied from the EDT to the worker thread. Make it volatile, or synchronize access to it, or use an AtomicReference, or read it in the EDT using SwingUtilities.invokeAndWait().
SwingWorker probably is the simplest way to go, here. Implement your time-consuming operation, and the on/off check, in the doInBackground() method, and your GUI update in the done() method.
public enum State {
RUNNING, STOPPED
}
public class ThreadSafeStateModel {
private State state = State.STOPPED;
public synchronized void stop() {
state = State.STOPPED;
}
public synchronized void start() {
state = State.RUNNING;
}
public boolean isRunning() {
return state == State.RUNNING;
}
}
public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
private final ThreadSafeStateModel model;
public ExpensiveProcessWorker(ThreadSafeStateModel model) {
this.model = model;
}
#Override // Runs in background
protected Void doInBackground() throws Exception {
while (model.isRunning()) {
// do one iteration of something expensive
}
return null;
}
#Override // Runs in event dispatch thread
protected void done() {
// Update the GUI
}
}
public class StopButton extends JButton {
public StopButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Stop") {
#Override
public void actionPerformed(ActionEvent e) {
model.stop();
}
});
}
}
public class StartButton extends JButton {
public StartButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
model.start();
new ExpensiveProcessWorker(model).execute();
}
});
}
}
(A lot could be done to clean this up depending on the real application, but you get the idea.)
#Override
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
//your code which runs on click event
}
}.start();
}
I have a jframe i want to display while my main frame is running. i want to pause my main code, until the user does the necessary actions on the other frame. I've read a lot of solutions but i need to see it done for my code to understand and grasp it fully. i do not want to use jdialog like I've seen listed as an answer before. My main goal is to understand better threading so that i can use what i learn in different cases.
With the code I've created, when running the thread, only just the frame loads, none of the other features are there on the frame. (the frame is simple it has a label, a list the user selects from, and a button to basically return the chosen list value.) its like the thread is cut off from completing or something.
here is my class calling the screen:
public class myThread implements Runnable {
String result = null;
public void run() {
MessageScreen ms = new MessageScreen();
ms.setVisible(true);
}
public String getResult() {
return result;
}
public void setResult(String AS) {
result = AS;
}
}
in my main code, a method is called that is returning a String[] value, with this method at some point i have the following code calling the new thread to get the value necessary to return in the original main method:
myThread mt = new myThread();
Thread t = new Thread(mt);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
myreturn = new String[] {"true", mt.getResult()};
without listing the whole code for the second frame, when the user presses the button, and at the end of the listener tied to the button press the i want to close the frame and return a string that was selected from the list:
jf.dispose();
myt.setResult(AdminSelection);
in the frame class, i have the following instance variables declared:
String AdminSelection = null;
myThread myt;
i hope this is enough information for someone to help me out and understand whats gone wrong here.
The function join() waits until the end of the run() method, when you do t.join(), your thread is already or almost ended. This is because in your run() method there is nothing that blocks the thread until the user has clicked the confirm button. And is better like this!
There is no sense to create a thread here, you should use a callback, or more generally in Java, a listener. You can take a look at Creating Custom Listeners.
But, especially if you want to pause your main code, you should use a (modal) JDialog which is made for this! Don't try to block the UI by yourself, you could block the UI thread (handled by Swing/AWT) by mistake. Creating a JDialog is better because everything is already made for this usage on the UI thread.
Also, you must know that create a Thread is really long, use a Thread when you really need it.
I'm developing a cli-based custom web crawler in Java. Since it needs to have a user-friendly graphical interface for showing the result, I should add a swing frame to that involving some trees, labels, tables and so on.
That graphical interface is one of its switches, and must be started just in case user calls it. Thus, I have to start this interface in a new thread so that the application can proceed with other tasks and update components inside of GUI frame if needs.
My GUI class is some thing like:
public class Frame extends JFrame {
......
public static JLabel urlLabel;
......
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
And, I fork it from my main class like this:
.....
if(cmd.gui){
Frame.run();
Frame.urlLabel.setText("New Url");
}
......
Unfortunately, the text of label doesn't change. I already tested repaint(), revalidate() and such other functions like these, but, nothing turned up.
I tested getText() in order to make sure it is possible to access urlLabel from main class, and it worked (I could retrieved text of label).
I was wondering how I can sort out this issue? (Basically, I need to start a swing frame in a different thread and control its components from the main thread)
Thanks in advance.
If you use invokeLater(), your Runnable will be started in the EventThread after the current operation in this thread is finished. If your label is not updated, it might be that your EventThread is busy doing something else - e.g. crawling the web.
You definitely need to make sure that your crawling work is done in another thread (start a new one, don't use the one that runs anyway, since this is most probably the EventThread). Then you might use invokeLater() in this other Thread to change the label in the EventThread.
Hint: You can check if you are in the EventThread by using SwingUtilities.isEventDispatchThread().
Remember that your data/models will be used by different threads and that this might cause problems when the data is changed in your worker thread while your GUI is trying to display it.
Thank you guys for helping.
Finally, I could overcome this obstacle by using SwingUtilities.invokeLater for updating the label's text.
I mention the approach here, since someone else might need it:
The main class:
public class Frame extends JFrame {
......
private static JLabel urlLabel;
......
public JLabel getU(){
return urlLabel;
}
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
The GUI class:
if(cmd.gui){
Frame().run();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
gui.getU().setText("New Url");
}
});
}
.....
Just a question about this manner:
Since I need to update some labels and tree nodes a couple of times during crawling, wanted to know if starting a new Runnable for each of those would be overload? If yes, how can I manage that?
---UPDATE---
According to the #xander's comment, it is possible to use lambda instead of Runnable. I think lambda doesn't have overload as much as a new object does.
I have something I can't understand: my Swing GUI contains a 'play' and 'pause' button. I have also a static variable that defines 'ON' and 'OFF' states. (The main program generates the GUI).
By cliking on 'play' I change the state of my static variable to 'ON' and I launch a time-consuming process in a thread that also modifies the GUI. As long as the static variable is 'ON' loops in the same process. Clicking on 'pause' would change the static variable to OFF.
But by clicking on 'play' the GUI is freezing and consequently:
The GUI doesn't update
The process can't be 'paused' with my 'pause' button.
I have heard about EDT and SwingWorker but I you have a simple way to do it I take it.
Thank you for your help and forgive my bad english...
The problem is that you're doing the intensive, time-consuming work on the same thread responsible for updating the GUI. SwingWorker allows you to move time-consuming tasks to a separate thread of execution, thereby leaving the UI thread to do its thing uninhibited.
However, it does add a further complication: affinity. Calling methods on UI components generally requires that you do so from the UI thread. Therefore, you need to use special functionality to get back to the UI thread from the worker thread. SwingWorker also gives you this ability.
I suggest you read through this documentation.
You need to read Concurrency in Swing to understand how the EDT and SwingWorkers operate.
All GUI updates are executed on the EDT so when you click a GUI component any method that this calls will be executed on the EDT. If this is a time consuming process then this will block the EDT from executing any futher GUI updates. Hence your GUI is freezing and you can't click the pause button.
You need to use SwingWorker to execute the time consuming process on another thread. The link I provided above details how to do this.
You should not start long-running processes in Swing’s event handler because it will freeze your GUI, you know that now. :) Start it in a new thread. You only need to use a SwingWorker if you’re planning on manipulating the GUI from the worker thread (because Swing is not thread-safe).
This is a pretty straightforward reason: while Java is working on your time-consuming process, it isn't able to update the GUI. Solution: run the time-consuming process in a separate thread. There are a bunch of ways to program that, and it would probably depend somewhat on how your program is written.
The event dispatch thread (EDT) is the only thread in which it's safe to read or update the GUI.
The pause button should be setting the on/off variable in the event dispatch thread.
The time-consuming operation, and the loop, should not be in the EDT. (The loop should also not be running continuously doing nothing but check the variable, or it can easily eat all your CPU. If it has nothing else to do it should check, and then call Thread.sleep() for some length of time (say 100ms).)
If you can prove that the on/off variable is being set to OFF, but that nonetheless it's always read as ON, it may be that the variable's value is not being copied from the EDT to the worker thread. Make it volatile, or synchronize access to it, or use an AtomicReference, or read it in the EDT using SwingUtilities.invokeAndWait().
SwingWorker probably is the simplest way to go, here. Implement your time-consuming operation, and the on/off check, in the doInBackground() method, and your GUI update in the done() method.
public enum State {
RUNNING, STOPPED
}
public class ThreadSafeStateModel {
private State state = State.STOPPED;
public synchronized void stop() {
state = State.STOPPED;
}
public synchronized void start() {
state = State.RUNNING;
}
public boolean isRunning() {
return state == State.RUNNING;
}
}
public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
private final ThreadSafeStateModel model;
public ExpensiveProcessWorker(ThreadSafeStateModel model) {
this.model = model;
}
#Override // Runs in background
protected Void doInBackground() throws Exception {
while (model.isRunning()) {
// do one iteration of something expensive
}
return null;
}
#Override // Runs in event dispatch thread
protected void done() {
// Update the GUI
}
}
public class StopButton extends JButton {
public StopButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Stop") {
#Override
public void actionPerformed(ActionEvent e) {
model.stop();
}
});
}
}
public class StartButton extends JButton {
public StartButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
model.start();
new ExpensiveProcessWorker(model).execute();
}
});
}
}
(A lot could be done to clean this up depending on the real application, but you get the idea.)
#Override
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
//your code which runs on click event
}
}.start();
}
I'm trying to add items to a JList asynchronously but I am regularly getting exceptions from another thread, such as:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 8
Does anyone know how to fix this?
(EDIT: I answered this question because it's been bugging me and there is no clear search engine friendly way of finding this info.)
Swing components are NOT thread-safe and may sometimes throw exceptions. JList in particular will throw ArrayIndexOutOfBounds exceptions when clearing and adding elements.
The work-around for this, and the preferred way to run things asynchronously in Swing, is to use the invokeLater method. It ensures that the asynchronous call is done when all other requests.
Example using SwingWorker (which implements Runnable):
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void> () {
#Override
protected Void doInBackground() throws Exception {
Collection<Object> objects = doSomethingIntense();
this.myJList.clear();
for(Object o : objects) {
this.myJList.addElement(o);
}
return null;
}
}
// This WILL THROW EXCEPTIONS because a new thread will start and meddle
// with your JList when Swing is still drawing the component
//
// ExecutorService executor = Executors.newSingleThreadExecutor();
// executor.execute(worker);
// The SwingWorker will be executed when Swing is done doing its stuff.
java.awt.EventQueue.invokeLater(worker);
Of course you don't need to use a SwingWorker as you can just implement a Runnable instead like this:
// This is actually a cool one-liner:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Collection<Object> objects = doSomethingIntense();
this.myJList.clear();
for(Object o : objects) {
this.myJList.addElement(o);
}
}
});
The model interface is not thread safe. You can only modify the model in EDT.
It is not thread safe because it asks the size separately from the contents.
Are you perhaps modifying it from another thread? Could perhaps be modifying it in the same thread during execution of a JList (or related) method that expects the contents to remain the same size.