In my program there are 4 threads that addElemets to a model of a jlist at the same time. This causes jlist to blink, throw excpetions or even crash, because of the too many updates or repaints.
I tried to put some delay that fixed my problem but I was losing to much precious time.
What can I do?
this code simulates my problem:
new Thread(new Runnable() {
#Override
public void run() {
for(int i=0; i<4; i++){
new Thread(new Runnable() {
#Override
public void run() {
for(int o=0; o<2000; o++){
model.addElement("add");
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}).start();
}
}
}).start();
The model.addElement("add"); should be something like this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
listModel.addElement("add");
}
});
This way you make sure that the elements are added under the EDT and not in some random thread.
GUI manipulations should only be performed on the event dispatch thread. Even if you have created many threads, do your non-GUI work on them n fire an actionCommand (or something similar) to manipulate GUI aspects.
You will get to read this warning many times in the Java Docs:
Swing components are not thread safe
To avoid too many refresh/repaint you have to do the model updates in the background threads and to copy once into the swing model in the EDT with the technique previously exposed. I suggest to do not use the DefaultListModel but a custom made to dispose of addAll().
SwingUtilities.invokeLater(new Runnable() {
public void run() {
listModel.addAll( backgroundModel ); // only one fireDataChanged() fired
}
});
Related
I'm trying to add a string to ListView in JavaFX whilst processing, but it keeps freezing my GUI.
I've tried the following threading - but can't seem to get it to work for a ListView.
Does anybody know how/have an example of how I can update a ListView in JavaFX whilst processing data?
new Thread(new Runnable() {
#Override public void run() {
for (int i=1; i<=1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
});
}
}}).start();
Using Platform.runLater() is the correct way to go.
You could, also, store the String result from Integer.toString(counter) in the background thread (not the UI one). By the way, you should use String.valueOf (there is a thread on StackOverflow which talks about it).
I assume your UI is freezing because of the execution speed of the (very simple) loop.
You should also have a look at Concurrency in JavaFX
You can do the animation / ui update after adding those strings in the list or use Platform.runLater only once (not advisable):
Platform.runLater(new Runnable() {
for (int i=1; i<=1000000; i++) {
final int counter = i;
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
}
});
Your GUI hangs because you are blocking the JavaFX application thread by calling Platform.runLater() continuously in your Thread.
You can perform a quick fix by adding a sleep statement inside your for-loop to avoid it.
for (int i=1; i<=1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
recentList.getItems().add(Integer.toString(counter));
}
});
// Add Sleep Time
Thread.sleep(some milli seconds);
}
To use a more proper and advisable way, use an AnimationTimer, as shown in
JavaFX - Concurrency and updating label
I am trying to enable/disable a label when a button is pressed and i want to do this during the event and not after it. As you can see below, i try to enable/disable the two labels: lblKeyboard and lblGamepad.
They end up running after the "RemoteControl.run();" is executed but i want it to happen before that. Any way i can do that?
Thank you!
JButton btnGamepad = new JButton("Gamepad");
btnGamepad.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(cont_state == 0){
if(RemoteControl.findGamePad() == true){
cont_state = 1;
game_status = "on";
}
else{
game_status = "off";
key_status = "on";
JOptionPane.showMessageDialog(null, "Controller not found!");
cont_state = 0;
}
}
if(cont_state == 1){
System.out.println("CONNECTED GAMEPAD!");
lblKeyboard.disable();
lblGamepad.enable();
frame.repaint();
RemoteControl.run();
cont_state = 0;
}
}
});
ActionEvents are run on the EDT which is also responsible for painting. Once you change the labels state, Swing issues a request for repaiting the Label. The thing is that this request is posted on a queue and will be executed once the EDT is free and, as you can see, the EDT is busy running your code so no repainting for you! Depending on the nature of your code, you should consider using a SwingWorker or simply moving RemoteControl.run() to another Thread as in
new Thread(new Runnable() {
#override
public void run() {
RemoteControl.run();
}
}).start();
Code in an event listener executes on the Event Dispatch Thread (EDT) and the GUI can't repaint itself until all the code has finished executing. Read the section from the Swing tutorial on Concurrency for more information on the EDT.
Try wrapping your RemoteControl.run() code in a SwingUtilities.invokeLater(...). This will place the code at the end of the EDT, which might give Swing a changes to repaint the state of the two labels.
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
RemoteControl.run()
}
});
This assumes your code updates the GUI. If not, then just use a separate Thread.
Is Java capable of creating more than one EDT at a time?
I'm experimenting with setting up EDT and how it works in updating the content of a "heavy duty" panel with potentially a dozen of panels embedded inside and with hundreds of components altogether. Currently I have
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.update();
}
});
}
I've looked at the following posts:
Measuring "busyness" of the event dispatching thread
How does the event dispatch thread work?
Java Event-Dispatching Thread explanation
http://en.wiki2.org/wiki/Event_dispatching_thread
and so forth.
I sort of understand that if there are, say a dozen of events, that an single EDT has to handle, Java already has an internal scheduling mechanism to group/prioritize these events.
According to http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html
"This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors."
So what if I create a 2nd EDT with new Thread(new Runnable() { ... }.start() below?
Will java automatically merge the two EDTs back to one for fear of thread safety?
new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.update();
}
});
}
}).start();
There can only be one Event Dispatch Thread!
But why would you even want to have more than one thread for this? Even for "heavy duty" panels with many components (in the application i am currently working on there must be 1000s of components) one EDT is enough. Remember you should not perform any tasks on the EDT that use a lot of CPU time. Otherwise you will block the EDT for update events and your GUI will become "sluggish" in responding to user input.
Also remember that all GUI components should be created and manipulated only from within the EDT because many components are not thread save. Ignoring this guideline may work for specific tasks but sooner or later you will get strange behavior and/or crashes!
The Swing GUI is single threaded. That single thread being the EDT. If you wanted to introduce a second EDT (and still have the GUI working) you would have to also rewrite a lot of the internal Swing code to account for the added complexity of thread safety.
Adding another EDT would introduce more complexity for an unknown amount of increase (or decrease) in performance.
The following applies in case one uses the Sun toolkit. I am not sure about other Java implementations (have not tested it).
One can have multiple EDT threads provided the toolkit is initialized from multiple threads from separate thread groups.
PoC code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] argv) {
for (int i = 0; i < 5; i++) {
// the separate thread group is needed at least for the Sun toolkit
ThreadGroup tg = new ThreadGroup("Test Group " + i);
Thread t = new Thread(tg, new Runnable() {
#Override
public void run() {
startApp();
}
}, "Test " + i);
t.start();
}
}
private static void startApp() {
sun.awt.SunToolkit.createNewAppContext();
final JFrame frm = new JFrame(Thread.currentThread().getName()) {
#Override
public void setVisible(boolean b) {
super.setVisible(b);
System.out.println("Closed");
if (!b) dispose();
}
};
final JTextArea ta = new JTextArea();
frm.add(ta);
JButton btn = new JButton("Dialog");
// Showing a modal dialog will block only this frame
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frm, "Test message from " + frm.getTitle());
}
});
frm.add(btn, BorderLayout.SOUTH);
frm.setPreferredSize(new Dimension(300, 300));
frm.pack();
frm.show();
Thread t = new Thread(new Runnable() {
int i = 0;
#Override
public void run() {
try {
while (true) {
i++;
if (!frm.isVisible()) break;
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
try {
ta.getDocument().insertString(0, "Test " + i + " " +
Thread.currentThread().hashCode() + // This is to show that we are actually on a different thread as invokeAndWait is static and one may suspect a different behaviour
"\n", null);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
Thread.sleep(1000 + (int)(Math.random() * 500));
}
Thread.sleep(4000); // This is just to show that the deamon thread might not have ended before the toolkit calls System.exit
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("Thread " + Thread.currentThread().getName() + " exit");
}
});
t.setDaemon(true);
t.start();
}
}
The above code demonstrates opening 5 frames on separate EDTs. When each frame is closed and disposed the respective EDT ends. When all the frames are closed and disposed the app will exit (automatically).
Notes:
one should not use (mix) components created in one EDT from another EDT;
closing the modal dialog from the example has the side effect of not returning the focus to the parent frame (as one would expect) but to any of the others.
I use the above approach in order to measure the height of a wrap enabled JTextArea that is populated with a multitude of different texts that might take some time and during that time I do not want to block the main UI. The result (the measured heights) is used on the main UI.
Tested with Java 1.6.0_25 and 1.8.0_60.
I have been trying this all day in different variations with little success. Could someone please help explain what I am doing wrong? I am just a beginner with regards to threads.
private JTextArea text = new JTextArea();
private JButton button = new JButton("Cancel");
public StatusFrame() {
text.setEditable(false);
this.add(text);
this.add(button, BorderLayout.EAST);
this.setSize(new Dimension(150, 100));
this.setVisible(true);
}
public void updateStatus(String textIn) {
text.setText(textIn);
}
public JButton getButton() {
return button;
}
In another class, I am calling methods which may take a while to complete. I want to be able to call the StatusFrame.updateStatus() method to keep the user informed on the progress.
This is what I have:
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
}
The frame appears but none of the code after the runnable appears to be run/processed. It just stops/blocks/something. But the GUI remains active
Thanks for any advice.
P.S.: I have tried using invokeAndWait() method but again not sure if I am doing it the right way. For now a quick fix would be preferred as I have not learned much about threads yet. Any instructions are welcome.
You have the concepts backwards.
Here's your code
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
You should execute the long running code in a thread, and use the SwingUtilities invokeLater method to update the GUI.
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
new Thread(r).start();
// inside the StatusFrame
//do something
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("process 1 completed");
}
);
//do something else sometime later
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("Process 2 completed");
}
);
I don't know if I was clear in my answer.
Execute SwingUtilities.invokeLater when you start your Java application to make sure Swing components are on the Event Dispatch thread (EDT).
From the EDT, invoke long running processes as a runnable thread.
In the runnable thread, since you're not on the EDT, execute SwingUtilities.invokeLater whenever you're updating Swing components. This ensures that Swing components are updated on the EDT.
Every Swing application should start with a class like this:
import javax.swing.SwingUtilities;
import com.ggl.text.entry.model.TextEntryModel;
import com.ggl.text.entry.view.TextEntryFrame;
public class TextEntry implements Runnable {
#Override
public void run() {
new TextEntryFrame(new TextEntryModel());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TextEntry());
}
}
This class does 3 things.
Constructs the GUI data model.
Constructs the GUI JFrame.
Ensures that the Swing components are on the EDT.
You'll need to call the updates on EDT too. I would suggest to sleep on the main thread, to give GUI a chance to show up before any other work:
someMethod() {
// prevent GUI from freezing using threads
Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
//do something
r = new Runnable() {
public void run() {
status.update("process 1 completed");
}
};
SwingUtilities.invokeLater(r);
//do something else
r = new Runnable() {
public void run() {
status.update("Process 2 completed");
}
};
SwingUtilities.invokeLater(r);
}
See Concurrency in Swing.
You may find using a Swing Worker easier to work with since it uses a Thread and has methods that will allow you to update the GUI properly.
In the following lines of code when Button1 is pressed the Jframe becomes unresponsive till the execution of the encode() method is completed. But I need to update a progress bar in the Jframe displaying the progress.
private void Button1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
if (flagState == 0) {
WavFile.decode(readWavFile, msg.getText(), key.getText().hashCode());
} else if (flagState == 1) {
WavFile.encode(readWavFile, msg.getText(), key.getText().hashCode());
}
} catch (WavFileException | IOException e) {
notes.setText(e.getMessage());
}
}
I thought of doing this
private void Button1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
if (flagState == 0) {
Thread t = new Thread(new Runnable() {
public void run() {
WavFile.decode(readWavFile, msg.getText(), key.getText().hashCode());
}
};)
t.start();
} else if (flagState == 1) {
Thread t = new Thread(new Runnable() {
public void run() {
WavFile.encode(readWavFile, msg.getText(), key.getText().hashCode());
}
};)
t.start();
}
} catch (WavFileException | IOException e) {
notes.setText(e.getMessage());
}
}
and update the progress bar from the encode() method.
But I understand that it will a strong coupling between the modules and i dont want that.
I also dont think that calling a new thread every time is a good idea ( thought of ExecutorService but not sure how to use it).
What should I do to make this into a decent piece code??
Threading is definitely something you have to introduce here. Anything that takes a significant amount of time to complete should not be run in the awt event thread.
Spawning a new thread each time the button is clicked is not inherently a problem. You should consider, though, that it may be possible to spawn many threads doing the same thing before even the first completes. You could use an Executor if you want, but it probably is not necessary.
As for tracking progress, I would add another thread that periodically queries the other thread and updates the progress bar as necessary. Here's how I would structure it:
private void Button1ActionPerformed(java.awt.event.ActionEvent evt) {
disableButton1();
final Thread t;
if (flagState == 0) {
t = new Thread(new Runnable() {
public void run() {
try {
WavFile.decode(readWavFile, msg.getText(), key.getText().hashCode());
} catch (WavFileException | IOException e) {
notes.setText(e.getMessage());
}
}
});
} else if (flagState == 1) {
t = new Thread(new Runnable() {
public void run() {
try {
WavFile.encode(readWavFile, msg.getText(), key.getText().hashCode());
} catch (WavFileException | IOException e) {
notes.setText(e.getMessage());
}
}
});
}
Thread monitor = new Thread(new Runnable() {
public void run() {
try {
while (notComplete(t)) {
setProgressBar(getProgress(t));
Thread.sleep(SLEEP_TIME);
}
t.join();
} finally {
enableButton1();
}
}
});
t.start();
monitor.start();
}
n the following lines of code when Button1 is pressed the Jframe becomes unresponsive till the execution of the encode() method is completed. But I need to update a progress bar in the Jframe displaying the progress.
you have issue with Concurency in Swing, you block the EDT, caused unresponsive GUI, until code ended
have look at SwingWorker for JProgressBar
use most scallable Swing Action instread of ActionListener
use implemented setEnabled() instead of if (flagState == 0) {
Multithreading is what you need, there is no other nice way i can think of don't worry about calling new threads, that's what they are there for, jvm has a thread pool ready to be used, this sort of things runs really good with threads, try it out!
Create a class that extends Runnable, implement the run method or make it call your current method, that should be enough to do this.
Also you could just make it sleep for a short time and check the status each time the sleep time goes out, if the process finished you can continue, if not, back to sleep for another short time, that way you don't have to ask that many times.
I hope it helps.