This may be a basic question but one that I am stuck on.
I would like some more understanding on why the GUI is blocked when I start a new thread(runnable) from a button click event? and how do I overcome that? What am I doing wrong?
The code below starts a new thread when it is clicked, however I would like to change the background color of a textbox when that button is clicked but I am unable to do that, also the main ui is unresponsive whilst that thread is running, I believed that I was implementing it correctly by starting a new thread so as to NOT block the main ui but I must have missed something out as this obviously not desired behaviour.
Code:
private void startButtonEvent(ActionEvent evt) {
ntmStatusText.setBackground(Color.green);// textbackground I want to change
//Start Ntm Thread
Thread thread = new Thread(new NtmThread());
thread.start();
while(thread.isAlive()){
System.out.println("thread still working");
}
System.out.println("thread stopped");
}
How do I stop my Ui from becoming unresponsive when running threads?
while(thread.isAlive()){ is a blocking method, it will stop the Event Dispatching Thread until Thread.isAlive becomes false, this prevents it from been able to process new events that are been added to the event queue, including paint requests
This occurs because Swing is a single threaded environment, meaning that there is a single thread responsible for process all events coming into the program. This is handled by the Event Dispatching Thread.
All user generated events are managed within the EDT (that is, you actionPerformed method, is called within the context of the EDT)
Concurrency in Swing
You eaither theen to provide a call back functionality to your NtmThread or consider using a SwingWorker and attaching a PropertyChanegListener to it a monitor for the property state and check that the SwingWorkers state has changed to DONE
See Worker Threads and SwingWorker for more details
As an example: How to use jProgress bar for ProcessBuilder process?
You have created a new thread but you are using current thread to check if the created thread is alive or not while(thread.isAlive()){.
So the current thread can never come out of the loop untill thread you created is not stopped.
And your this current thread was responsible for doing all gui work, but you made it stuck, that is why your GUI is blocked.
Related
I am multithreading a background process on a JFrame window, and I want to update the text of a global JLabel variable inside a function.
So when the thread runs, my while and for loops run, but during them running, it makes the call to that other function to change the text.
However, it doesn't update until the thread finishes executing (i.e when the loops finish). Any reason why this happens, and how I can fix this?
If I try to just change the text of the JLabel inside the thread, it has the same result.
What might happened is your thread did make the change, but the screen display is not updated until the Swing event dispatch thread comes around and updates the display. It is recommended and safer to call user interface updating methods using the Swing event dispatch thread. You can do this by putting gui update code inside this method:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// call gui update here
}
});
This still doesn't solve your problem yet (update happens after thread finish executing). This is only to make it explicit that you are dealing with two threads here. Your for loop on a separate thread will likely to finish first because the drawing thread is usually slower. However, knowing this will help you to plan which code to be passed to Swing event dispatch thread. If you really want the for loop to update at that point in time, then you can put the entire for loop inside the invokeLater().
This may be a basic question but one that I am stuck on.
I would like some more understanding on why the GUI is blocked when I start a new thread(runnable) from a button click event? and how do I overcome that? What am I doing wrong?
The code below starts a new thread when it is clicked, however I would like to change the background color of a textbox when that button is clicked but I am unable to do that, also the main ui is unresponsive whilst that thread is running, I believed that I was implementing it correctly by starting a new thread so as to NOT block the main ui but I must have missed something out as this obviously not desired behaviour.
Code:
private void startButtonEvent(ActionEvent evt) {
ntmStatusText.setBackground(Color.green);// textbackground I want to change
//Start Ntm Thread
Thread thread = new Thread(new NtmThread());
thread.start();
while(thread.isAlive()){
System.out.println("thread still working");
}
System.out.println("thread stopped");
}
How do I stop my Ui from becoming unresponsive when running threads?
while(thread.isAlive()){ is a blocking method, it will stop the Event Dispatching Thread until Thread.isAlive becomes false, this prevents it from been able to process new events that are been added to the event queue, including paint requests
This occurs because Swing is a single threaded environment, meaning that there is a single thread responsible for process all events coming into the program. This is handled by the Event Dispatching Thread.
All user generated events are managed within the EDT (that is, you actionPerformed method, is called within the context of the EDT)
Concurrency in Swing
You eaither theen to provide a call back functionality to your NtmThread or consider using a SwingWorker and attaching a PropertyChanegListener to it a monitor for the property state and check that the SwingWorkers state has changed to DONE
See Worker Threads and SwingWorker for more details
As an example: How to use jProgress bar for ProcessBuilder process?
You have created a new thread but you are using current thread to check if the created thread is alive or not while(thread.isAlive()){.
So the current thread can never come out of the loop untill thread you created is not stopped.
And your this current thread was responsible for doing all gui work, but you made it stuck, that is why your GUI is blocked.
I have three questions which are closely related in that they are born out of each other and represent a train of thought, so I am posting them under one question. It would not help me construct the big picture of my question if I posted them separately.
1) Could you please explain in simple language what SwingUtilities.invokeLater does? I understand threading, dare I say quite a bit, but still the language of the documentation confuses me. It says:
Causes doRun.run() to be executed asynchronously on the
AWT event dispatching thread. This will happen after all
pending AWT events have been processed. This method should
be used when an application thread needs to update the GUI.
In the following example the invokeLater call queues
the Runnable object doHelloWorld
on the event dispatching thread and
then prints a message.
If I put some effort in to make sense of what that says, I think here is what it says, but I couldn't be so sure about it. I think it says:
The invokeLater method schedules the main window creation and the setting up of its dispatcher / message pump on the primary thread of the application only and not on a separate thread. It does it by posting the message to create the window and set it up on the main / primary application thread. In other words, the main thread is saying to us, "The window you are asking me to create will be created after I am done doing everything else that is on my plate right now."
But then two things confuses me, which I list as the two questions below.
2) Then why do I need to implement the new window's message loop as a Runnable. This implies that I want a separate thread to execute that message loop.
3) I printed out the current thread Id's in the function that creates the window and the function that is the window's message loop, and they are both different threads. So, each window in Swing runs on its own thread? That is insane. Can you please explain to me what is happening here? And also if you could please explain in a paragraph or two the threading model of GUI applications created in Swing?
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainWindowEventLoop());
System.out.println(String.format("Main thread %1$d started.",
Thread.currentThread().getId()));
}
public class MainWindowEventLoop implements Runnable {
#Override
public void run() {
JFrame mainWindow = new MainWindow("Main Window");
System.out.println(String.format("Main window loop running on thread %1$d.",
Thread.currentThread().getId()));
}
}
Output:
Main thread 1 started.
Main window loop running on thread 14.
It's a little complicated, but Swing is not thread safe. To run the GUI asynchronously and safely, Sun/Oracle uses a locking pattern called Ad-Hoc Thread Confinement. All Swing components must run on the AWT EDT (Event Dispatch Thread) or the result is not thread safe.
Here's a link to Oracle's tutorial. Try to read all of those sections, see if it makes more sense.
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/
Each window does NOT run on its own separate thread. There is only one EDT. Each windows runs on the SAME thread, the EDT. Each Runnable you send to the EDT is executed sequentially, one after the other, when the EDT has the opportunity to do so. Hence the "later" part of invokeLater().
Basically all the swing windows are bounded to main thread. Every single component in swing runs as thread. After the completion of an event control again returns back to main thread which waits for an event to occur.
It seems I miss a certain point in Java and Swing. The issue is as follows:
I have a swing GUI with a Start / Stop button (And some more)
It starts the function (What is set up as a thread in this case), Thread starts, it works, Swing GUI is fully operational and does all the other things it is made for, e.g. modifying parameters for the worker thread.
Of course, sending an interrupt to ask the thread to stop on users request is in and functions. So far so good.
But I did not found a way that Swing GUI notices the thread stopped on its own:
If I ignore it, it confuses the user.
I of course put a loop in, where Swing GUI regularly asks if Thread.isAlive (And sleeps some time to ask again), but this loop completely blocks Swing GUI.
Ideally, I would like to get a notification or an event that the thread has stopped.
Just like all the other events, Swing processes:-) .
What is the proper way to set it up?
Have a look at the SwingWorker. It is designed to perform tasks on the background as the result of a Swing event such a button press. It has hooks to listen for when the task finishes.
use a boolean flag 'done' private field. initialize done=false; when your thread run() method is complete set done=true inside thread run() method.
monitoring the value of the flag 'done' will tell you when your thread has finished.
I have read that the main UI thread in android should not call sleep.
However, my application needs to :
call thread1 from main UI thread
call thread2 from main UI thread.
Use the output (2 images) of the 2 thread, add them and then display them.
I am using Thread.sleep() so that the main thread waits thread1 and thread2 till they are done. However mImageview.setbitmap is not working after i call it in the main thread (after sleep).
can u pls advise me how i should do this?
Multithreading in Android should be done Asynchronously. For this purpose, you should use the AsyncTask-class.
For your case, you would for example create a taks to load (or process) those two images. While the process is running (in another thread, off the UI-thread), you could show a ProgressBar that shows to the user that your application is currently busy (this is then done on the UI-thread).
When the task has finished, you get the results (your two images) from the task, hide the progress-bar and show everything to the user.
Despite the fact that a non-reacting GUI always has the feeling that your application frooze, if a UI-Thread is blocked for more then 5 seconds (which is a looong time), your application will be Force-closed as it is not "reacting" (and an ANR will be raised).
It's not just Thread.Sleep(). In the GUI thread, do whatever you need to to start the two threads/tasks/whatever and then exit the event handler.
Do not wait in GUI event-handlers! Not in Java, C++, C, Delphi, anything. Use an async task, or a Handler, and signal to the GUI thread. Thread 1 signals that it is done, thread 2 signals that it is done. In either case check to see if the data has been returned by the other thread. If it has, you have both sets of returned data and so you can add them and display them.
Do not wait in GUI event-handlers.
You can simply use Threads And Handlers for this purpose.
Here is a small demo for this,
Create a Handler in your onCreate like this,
Drawable d=null;
Handler handler=new Handler()
{
public void handleMesaage(Message msg)
{
if(msg.what==0)
{
imageView.setBackgroundDrawable(d);
}
}
};
And now call Your Thread like this,
Thread t=new Thread(new Runnable()
{
#Override
public void run() {
InputStream is = (InputStream) new URL(url).getContent();
d = Drawable.createFromStream(is, "src name");
handler.sendEmptyMessage(0);
}
});t.start();
I would suggest to use the ExecutorService. Here is how
Create the two image loading activities as Runnable tasks.
Execute them with the ExecutorService.
Use ExecutorService.awaitTermination(); for the main thread to wait for Runnable tasks to complete. Its documentation reads
Blocks until all tasks have completed execution after a shutdown
request, or the timeout occurs, or the current thread is interrupted,
whichever happens first.
This is the Asynch way of doing it and I guess should be preferred.