Java is not my mother tongue and I've been fighting with this problem for a little while.
Basically, I am finding a behavioural difference between calling method switchApplets() directly from init(), and calling it from within a new thread spawned by init().
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser. If called at the end of init(), the new UI renders immediately without any input from the user. But that's not an option because it doesn't wait for the thread to finish its prep work.
Trimmed-down code:
public class PreLoader extends Applet implements AppletStub {
static JProgressBar pBar = null;
static JLabel message;
public void switchApplets() {
try {
Class main_class = Class.forName("MainClass");
Applet main_applet = (Applet)main_class.newInstance();
removeAll();
setSize(0,0);
setLayout(new GridLayout(1,0));
add(main_applet);
main_applet.init();
main_applet.start();
main_applet.setStub(this);
}
catch (Exception e) {
}
}
public void init() {
pBar = new JProgressBar(0, 100);
pBar.setValue(0);
pBar.setStringPainted(true);
message = new JLabel("Beginning work!");
add(message);
add(pBar);
FlowLayout flow = new FlowLayout();
setLayout(flow);
Thread t = new Thread ( new Runnable () {
public void run ()
{
longRunningFunction1();
longRunningFunction2();
message.setText("Work complete! Stand by..");
switchApplets(); //does NOT work as intended from here
return;
}
} );
t.start();
//switchApplets(); //works as intended if called HERE
}
public void longRunningFunction1() {
//perform some tasks, advance progress bar
}
public void longRunningFunction2() {
//perform some tasks, advance progress bar
}
public void start() {
return;
}
public void appletResize(int width, int height) {
return;
}
}
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating. Also tried playing with SwingUtilities' invokeLater/invokeAndWait, but even though switchApplets() gets run on the EDT, it seems that it MUST be called directly from init() (or at least the thread init is running on) to have the desired effect.
Why does calling switchApplets() from within a new thread result in a slightly different (and unwanted) UI behaviour?
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser.
It's likely a deadlock caused by trying to do UI code on the wrong thread.
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating.
You're on the right track. You need to call switchApplets() only from the EDT, and only after the work is done on the other thread.
Are you sure you tried using invokeLater() or invokeAndWait() from within the spawned thread after the long running functions were done? It's been a long while since I did applets but I'm not aware of any applet-specific reason why it wouldn't work, and it would work in any other case. I.e.,
public void run()
{
longRunningFunction1();
longRunningFunction2();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
message.setText("Work complete! Stand by..");
switchApplets();
}
});
}
However, the most proper way to do this is with a SwingWorker rather than a manually created thread. SwingWorker (which is not nearly as well-known as it should be) is designed exactly for the goal of performing background tasks on a separate thread while still being able to update the GUI with progress updates and the results. E.g.,
new SwingWorker<Void,Void>() {
#Override
protected Void doInBackground() { // is called on a background thread
longRunningFunction1();
longRunningFunction2();
return null;
}
#Override
protected void done() { // is called on the Swing thread
message.setText("Work complete! Stand by..");
switchApplets();
}
}.execute();
The Void stuff is because SwingWorker is also capable of returning results and sending intermediate progress updates, but this example doesn't use those features.
You indicated that your long running functions are also updating a progress bar. That's another thing that should happen only on the Swing thread. In practice you can often get away without it, but it's dodgy. Your progress updates can use one of the SwingUtilities.invoke methods, or the mechanisms of SwingWorker; either should work. (SwingWorker itself provides two different ways to do it: Call addPropertyChangeListener (Swing thread) and setProgress (background thread), or call publish (background thread) and override process (Swing thread).)
Also, a small suggestion: if it's inconvenient to deal with a checked exception (or impossible to usefully do so), rather than catching and ignoring it, you should at least catch & rethrow it as an unchecked exception:
catch (Exception e) {
throw new RuntimeException(e);
}
That way, the stacktrace and error message of any exception will not be lost.
Related
I have a project that takes time to load everything so I create a splash screen that tells the user through a progressbar how much time it will take to fully load and show the UI, but I'm facing a problem.
When I create my splash, this shows up correctly but then I create and initialize the Principal frame and everything freeze until this has fully load.
So, I try to load my Principal frame in a thread using SwingWorker (and it works) but after unknown NullPointerExceptions and reading a lot I found that this is a terrible idea because I am not creating my UI in the EDT, so here I am stuck.
I know that I must do Swing Calls in the Event Dispatch Thread (EDT) and non-swing heavy work in SwingWorkers but initialize the Swing Components of my Principal Frame are a heavy work too so, what should I do?
I have read some question here, specially this, and I think I get it but I have doubts. Taking that example:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new SplashScreen();
}
});
// Code to start system (nothing that touches the GUI)
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new MainFrame();
}
});
//.. etc
And reading this site that says:
The Swing framework manages component drawing, updates, and event handlers on the EDT.
Is creating a new component a Swing Call? If it is, What should I do if new MainFrame() will take some time because the project has a lot of components to initialize?
How do I tell the Splash something like "Program loaded 50%"?
What does a Swing Call means and how can I do a correct use of invokeLater and SwingWorker? Maybe the solution is too obvious or have already an answer, but I can't see it and I apologize if this is the case.
Thanks!
You're on a right track. But don't use invokeAndWait (if you have to only) - use invokeLater:
invokeAndWait
Causes doRun.run() to be executed synchronously on the AWT event dispatching thread.
invokeLater
Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread.
Consider that block wrapped doLater is run on EDT thread and code wrapped in doOutside is invoked in another thread (and that's why you don't block the UI):
EDIT:
As pointed out in the comments I add the explanations for the concepts I'll use.
doLater {
// here goes the code
}
is a concept for:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// here goes the code
}
});
And
doOutside {
// here goes the code
}
is a concept for:
new Thread(new Runnable() {
#Override
public void run() {
// here goes the code
}
}).start();
doLater {
final MainFrame m = new MainFrame();
doOutside {
// handle heavy operation
final int result = 1;
doLater {
m.setResult(result);
}
}
}
Conclusion: everything that touches Swing in some way must be run on EDT.
If you want to update percentages:
doLater {
final MainFrame m = new MainFrame();
doOutside {
// handle progress
for(int i = 0; i < someSize; ++i) {
final int progress = i;
doLater {
m.getProgressBar().setProgress(progress);
}
}
}
}
I hope you understand the concept now. The SwingWorker just do exectly something as doOutside === doInBackground & doLater === done/progress
Btw. The code above is a real code: lookup Griffon framework in Groovy.
I have a task that can take a few seconds to a few minutes, and when I click on the button to execute the task, it runs the task but does not always disable button A and enable button B.
Here is the code that I am using:
#FXML
public void onExecute(ActionEvent event){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
#Override
public void run(){
Platform.runLater(() -> {
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
If I comment out the button disabling in Platform.runLater() button A gets disabled and button B get enabled, but after Platform.runLater() runs. Why does this work sometimes and not others?
According to the Javadocs for Platform.runLater(...), it
Runs the specified Runnable on the FX Application Thread
So the only thing your background thread does is to schedule all your time-consuming database work to run on the FX Application Thread: your background thread is basically redundant, and your UI will be unresponsive while the database work is running.
If it happens that a frame is rendered between the calls to btnExecute.setDisable(true); and the runnable you define getting executed, then you will see the disabled state change. If not, then all your code gets executed during the same frame rendering(*), so you will never see the disabled state change.
Platform.runLater() should be called from a background thread just to update the UI. So you could make this work as follows:
#FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
#Override
public void run(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
Platform.runLater(() -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
Platform.runLater(...) is a pretty low-level approach for FX work. The javafx.concurrent package defines a higher-level API for this: in particular the Task class encapsulates a background task and provides callbacks that will be executed on the FX Application Thread so you can update the UI. The Javadocs have copious examples, but you could do:
#FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
Task<Void> databaseTask = new Task<Void>() {
#Override
public void call(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
return null ;
}
};
databaseTask.setOnSucceeded( event -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
new Thread(databaseTask).start();
}
(*) This is a somewhat imprecise statement, but it's qualitatively correct. Technically, the rendering thread blocks while actions are being executed on the FX Application Thread. (The two are not the same thread, but they have a large amount of synchronization between them.) Thus it's impossible for a frame to be rendered between the call to QueryTable qt = new QueryTable(...); and btnStopExec.setDisable(true);. It is possible for a frame to be rendered between btnStopExec.setDisable(false); and the execution of your runnable (i.e before QueryTable qt = new QueryTable(...);). If such a frame is rendered, you see the disabled state change; if not, you don't. Whether or not that happens is just down to the timing of the calls with respect to the "pulses" (frame renderings), which are targeted to happen every 1/60th second.
I am working on a relatively simple DB manager, that takes in a number of files, parses and catalogs the information in a particular fashion. I also wrote a simple GUI in Swing for this purpose. In order to speed up the process I want to implement multithreading to the parallelizable parts of the execution in order to speed up the program.
The below code sits in a class called FDBCreatePanel, a custom JPanel, that sits in a FDBManagerFrame which accommodates the main method.
private void dbCreateActionButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dbCreateActionButtonActionPerformed
jfc = new JFileChooser();
jfc.setVisible(true);
jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = jfc.showSaveDialog(null);
((FDBManagerFrame) SwingUtilities.getRoot(this)).startProcessAnimation();
if(returnVal == JFileChooser.APPROVE_OPTION) {
new SwingWorker<Void,Void>(){
#Override
protected Void doInBackground() throws Exception {
File dir = jfc.getSelectedFile();
DbManager dbm = new DbManager(dir, dbNameField.getText());
try{
dbm.doTimeConsumingStuff();
} catch (SQLException e){
// errorhandling
}
#Override
protected void done() {
((FDBManagerFrame) SwingUtilities.getRoot(FDBCreatePanel.this)).endProcessAnimation();
}
}.execute();
}
}
The time consuming method in DbManager class leads to (among others) the following bits of code in the ParserType1 class:
private void init() {
try {
this.reader = new LineNumberReader(new FileReader(this.datfile));
Thread t = new Thread(new Runnable(){
#Override
public void run() {
Entry e;
while((e = parseNextEntry()) != null)
queue.offer(e);
}
}, "t1-parser-thread");
t.run();
} catch (FileNotFoundException e) {
// error handling
}
}
I do not see any t1-parser-thread(s) in JVisualVM when I monitor the execution of my program. It appears as if my code executes entirely on a single thread, ignoring the initiation of new threads. Am I missing something with respect to threading and Swing?
You're calling run() on the newly created Thread object in ParserType1.init(). That doesn't start a new thread - it just execute's the thread's run() method in the existing thread. You should be calling start() instead.
Fundamentally I think it was a mistake for Thread to implement Runnable at all - the distinction between "this is the code that should be executed" (Runnable) and "this is the way I'm going to execute it" (Thread) has been unfortunately blurred. The fact that Thread can also compose a Runnable makes it even worse.
Runnable runnable = new Runnable() { ... };
Thread thread = new Thread(runnable) {
// Override run here...
};
Unless your overridden run method calls super.run(), the Runnable passed into the constructor is ignored. Crazy stuff. Thread should (IMO) be final, not implement Runnable, and force you to provide a Runnable at construction. It's far too late to change now, unfortunately :(
Basically, you should never be calling run() on a Thread. At least, I can't remember the last time I saw that without it being a bug.
As Jon Pointed out you want to call the start() method to actually spawn a new Thread which will call the run method of your inline Runnable. If you just call run it is like you called any other method and it will execute in the same Thread.
}, "t1-parser-thread");
t.start();
http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
I'm making a chess program for a project. I'm trying to add a move history box to the side of the board. The move history works fine, and the data is properly sent to the text area, but the text inside the JTextArea disappears while the AI is thinking about his move.
public void aiMove(){
if (!playing){ return; }
paintImmediately(0,0,totalX,totalY);
ai = eve.getMove(chess,wtm,aiOut); //text disappears here
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
writeMove(ai); //updates move history, text reappears here
playing = stillPlaying();
repaint();
}
private void writeMove(Move move){
char c = "abcdefgh".charAt(7-move.fromY);
char h ="abcdefgh".charAt(7-move.toY);
String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+" ";
if (!wtm){
String q = chess.getFullMove()+". "+s+" ";
moves.setText(moves.getText()+q);
}
else {
moves.setText(moves.getText()+s+"\n");
}
}
Here's a print screen of what's happening.
http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png
SOLVED
Thanks to all replies. I changed aiMove() so it creates a thread. Here is what I did.
Attempt #3... swing is still so foreign to me. I didn't want to change writeMove to getMove or I would have to rewrite the human's turn slightly. Since the project is essentially done, I am trying to avoid as much work as possible :)
The GUI is entirely optional anyways, I was just doing it for fun, and to try and learn a bit of swing.
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
It also fixed a problem I had before, in that if I were to hold down the 'a' key (force ai move), it would queue up many forced ai moves. Now that doesn't happen.
The problem is your AI thinking is CPU intensive/time consuming, thus it is considered a long running task. You should not do long running tasks on GUI Event Dispatch Thread as this will cause the UI to seem frozen and thus only show updates after the task has finished.
Fortunately there are 2 different approaches you could use:
Use a Swing Worker which as the tutorial states:
The SwingWorker subclass can define a method, done, which is
automatically invoked on the event dispatch thread when the background
task is finished.
SwingWorker implements java.util.concurrent.Future.
This interface allows the background task to provide a return value to
the other thread. Other methods in this interface allow cancellation
of the background task and discovering whether the background task has
finished or been cancelled.
The background task can provide
intermediate results by invoking SwingWorker.publish, causing
SwingWorker.process to be invoked from the event dispatch thread.
The background task can define bound properties. Changes to these
properties trigger events, causing event-handling methods to be
invoked on the event dispatch thread.
Alternatively create separate Thread for AI thinking and wrap setText call in SwingUtilities.invokeLater(...);
Thread t=new Thread(new Runnable() {
#Override
public void run() {
}
});
t.start();
UPDATE
After reading MadProgrammers comment (+1 to it) please remember to create/manipulate your GUI/Swing components on EDT via the SwingUtilities.invokeLater(..) block. You can read more on it here.
UPDATE 2:
That edit is defeating the point, the only call on EDT in SwingUtilitites block should be the setText or atleast only code that manipulates a Swing component i.e
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){ //originally initialized by constructor
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
I have some code which takes a few minutes to process, it has to connect to the web for each string in a long array, each string is a url. I want to make it so that everytime it connects, it should refresh the jtextarea so that the user is not staring into a blank page that looks frozen for 20 min. or however long it takes. here is an example of something i tried and didnt work:
try {
ArrayList<String> myLinks = LinkParser.getmyLinksArray(jTextArea1.getText());
for (String s : myLinks) {
jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(jTextArea1, "Parsing Error", "Parsing Error", JOptionPane.ERROR_MESSAGE);
Logger.getLogger(MYView.class.getName()).log(Level.SEVERE, null, ex);
}
The problem is that you need to perform the computation asynchronously. You should create a background thread that performs the computation, and then use SwingUtilities.invokeLater to update the JTextArea.
final ArrayList<String> myLinks = //...
(new Thread()
{
public void run(){
for (String s : myLinks) {
try{
final String result = LinkChecker.checkFileStatus(s) + "\n";
SwingUtilities.invokeLater(new Runnable(){
public void run(){
jtextArea2.append(result);
}
});
}catch(IOException error){
// handle error
}
}
}
}).start();
Edit
It has been pointed out that JTextArea's append function actually is thread safe (unlike most Swing functions). Therefore, for this particular, case it is not necessary to update it via invokeLater. However, you should still do you processing in a background thread so as to allow the GUI to update, so the code is:
final ArrayList<String> myLinks = //...
(new Thread()
{
public void run(){
for (String s : myLinks) {
try{
jtextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}catch(IOException error){
// handle error
}
}
}
}).start();
However, for pretty much any other operation that modifies a Swing object, you will need to use invokeLater (to ensure the modification occurs in the GUI thread), since almost all the Swing functions aren't thread safe.
You need to investigate threading and its relationship to GUI updates in Swing. Anything that affects or makes use of GUI components in Swing must done on a special thread called the Event Dispatch Thread (EDT).
If your code snippet, if it's freezing the GUI, I imagine that it is being run in the EDT. Performing a long-running action on the EDT will make the GUI unresponsive, because no further updates can be done while your long-running process is using the thread.
There is a helper class called SwingWorker that allows you to offload long-running computations to a background thread, and then make updates to the GUI thread when it is complete. The SwingWorker looks after the context switches between the GUI thread and the background thread. You can also display progress bars to let the user know the state of the long-running process, so they know your application hasn't hung.
swing/awt is a single threaded library, so once a component is shown, just changing it's appearance won't work correctly. You need to change the component on the GUI Thread, not from your thread. To do this wrap any code that updates a component with SwingUtilities.invokeLater... as in
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}
});
also you want to limit what you do on the gui thread to avoid the gui from becoming sluggish, so if checkFileStatus is time consuming, execute it outside the run method and store the result in a final local variable, and just access the variable in the run() code.