How to know when two threads are finished in Swing - java

I have to perform two tasks. I like two threads perform each task simultaneously. The tasks don't share data.
Before the tasks start, is shown a dialog with a info "Wait, processing...".
Here the codes:
final JDialog dialog = new JDialog(this, true);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
}
};
SwingWorker<Void, Void> worker2 = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
}
};
worker.execute();
worker2.execute();
dialog.setVisible(true);
// Must close dialog?
I would like to close the dialog only when the two threads ended. How to know when they ended? Who and when should close the dialog?
Update: the threads must run simultaneously, not in sequential mode.

Create a CountDownLatch, set to 2
Create your two SwingWorkers, passing each a reference to the CountDownLatch. In there done methods, call countDown on the latch. Do this in the done method, as it will be called regardless of how the doInBackground method exited (ie in case it throws an Exception)
Create a third SwingWorker, passing it a reference to the CountDownLatch, in this worker wait for the latch in the doInBackground method. Once this SwingWorker's done method is called, you should now be able to dispose of the dialog safely

You should call get() on both workers

For now I have made a sample code which will help you to understand the logic behind this.
import java.awt.BorderLayout;
import javax.swing.*;
public class DemoTest {
JFrame frame = new JFrame();
JLabel lbl1 = new JLabel();
JLabel lbl2 = new JLabel();
SwingWorker<Void,Void> worker1 = new SwingWorker<Void,Void>()
{
#Override
protected Void doInBackground() throws Exception {
for(int i = 0;i<=100;i++)
{
lbl1.setText("Counter1 Value:"+Integer.toString(i));
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void done()
{
lbl1.setText("Thread 1 completed its job");
worker2.execute();
}
};
SwingWorker<Void,Void> worker2 = new SwingWorker<Void,Void>()
{
#Override
protected Void doInBackground() throws Exception {
for(int i = 0;i<=100;i++)
{
lbl2.setText("Counter1 Value:"+Integer.toString(i));
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void done()
{
lbl2.setText("Thread 2 completed its job");
}
};
public DemoTest()
{
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(lbl1,BorderLayout.NORTH);
frame.add(lbl2,BorderLayout.SOUTH);
frame.setVisible(true);
try
{
worker1.execute();
}
catch(Exception e)
{
e.printStackTrace();
}
//close dialog box
}
public static void main(String []args)
{
DemoTest d = new DemoTest();
}
}

I would use something like a counting lock for this. It is definitely using the least possible resources. The class below is a counting lock. Basically you initialise it with the constructor and specify the number of threads you need to wait for.
In the main thread (or UI thread) you call "waitForAll()" once you are done with setup. You can see that waitForAll is basically waiting for a notify from any other thread. If a notify is received it checks whether or not the number of active workers has reached zero. If the number of active workers is still greater 0 it waits again.
The workers however call unlock() on the lock. Notify decreases the counter by one and calls notify() which makes the main thread wake up and perform the above mentioned procedure.
public class CountingLock {
private int counter;
/**
* Number of workers
*
* #param n
*/
public CountingLock(int n) {
this.counter = n;
}
/**
* Wait until counter == 0
* #throws InterruptedException
*/
public synchronized void waitForAll() throws InterruptedException {
while(counter > 0) {
this.wait();
}
}
/**
* Deduce counter and notify
*/
public synchronized void unlock() {
this.counter--;
this.notify();
}
}
In the dialog prior launching the threads do the following:
CountingLock lock = new CountingLock(2);
/** put your thread setup code from your example here */
lock.waitForAll();
dialog.setVisible(false);
Make sure to pass a reference of lock to your threads and at the end of each thread call the following:
lock.unlock();
As per the comment to this answer, Java as of Java 1.5 (verified) provides a class java.concurrent.CountDownLatch with the exactly same behaviour. The use is well documented in the API.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html
example with CoundDownLatch
CountDownLatch lock = new CountDownLatch(2);
/** put your thread setup code from your example here */
lock.await();
dialog.setVisible(false);
In the threads do the following:
lock.countDown();
full example
final CountingLock lock = new CountingLock(2);
final JDialog dialog = new JDialog(this, true);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
lock.unlock();
}
};
SwingWorker<Void, Void> worker2 = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
lock.unlock();
}
};
worker.execute();
worker2.execute();
dialog.setVisible(true);
lock.waitForAll();
dialog.setVisible(false);
Actually you should also consider to move the waitForAll or await call and setting dialog.setVisible(false) in another background thread since you most likely will not want the UI to stall.

Related

How to pause a thread using wait and notify JavaFX

I am unclear on how to use wait() and notify() to pause a thread. I read talk about synchronizing, but I'm unsure how to do it in my instance. I have a music player with a progress bar where I want to pause the thread in control of syncing the progress bar with the music. Here is the thread I want to pause:
#FXML private void clickedButton(ActionEvent event){
shuffle.setOnAction(e -> {
artistPane.setText(model.getCurrentSong());
if(firstTime){
//Multithreading with JavaFX. Essentially this other thread will check the slider to make sure its on track.
sliderThread = new Task<Void>() {
#Override
protected Void call() throws Exception {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
//this is an infinite loop because now I only need to make this thread once, pausing and starting it, as opposed to making many threads
for(;;){
Thread.sleep(100);
progressBar.setValue(controller.getPercentageDone());
}
}
};
new Thread(sliderThread).start();
firstTime = false;
}else if(!model.getIsPlaying()){
//I want to start the thread here
}
controller.shuffle(); //this will start the music on the next song
});
Here is the second half where I also want to pause and start the thread:
play.setOnAction(e -> {
controller.play(); //this will pause/start the music
if(!model.getIsPlaying()){
//where I want to pause the thread.
}else{
//I want to start the thread here
}
});
I will try to give you simple example and you try to apply it to your program...
public class TestClass extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private Thread playThread ;
TestClass() {
playThread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println("DO SOME THING HERE");
System.out.println("SONG WILL PLAY.....");
}
});
}
public void startMyPlayer() {
System.out.println("PLAYING NOW...");
playThread.start();
}
public void pauseMyPlayer() throws InterruptedException {
System.out.println("PAUSED NOW...");
playThread.wait();
}
public void resumeMyPlayer() {
System.out.println("RESUMING NOW...");
playThread.notify();
}
}
That's it.I hope this help you.

JTextfield looping text issue

I have created a GUI using swings package containing a button and a text field, and have also added event handling on button such that when it gets clicked, the textfield should display a message continuously for 5 times as it is in loop.
public void actionPerformed(ActionEvent ae){
for(int i=0;i<5;i++){
tx.setText("Running"+i);// here tx is the JTextField object
}
if you wish to show it as an animation, you have to do it at background or another thread.
here is a sample
private Task task;
private void ButtonActionPerformed(java.awt.event.ActionEvent evt)
{
task = new Task();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
task.execute();
}
class Task extends SwingWorker<Void, Void>
{
#Override
public Void doInBackground() throws Exception
{
for(int i=0;i<5;i++)
{
Lab.setText("Running"+i);
Thread.sleep(200);
}
return null;
}
}
Use Runnable and put thread inside it..
Runnable run = new Runnable() {
#Override
public void run() {
for(int i=0 ; i<5;i++){
try {
Thread.sleep(1000); //time to wait
jTextField_Cost.setText("Running"+i+"");
}catch(InterruptedException e1){
e1.printStackTrace();
}
}
}
};
ExecutorService _ex = Executors.newCachedThreadPool();
_ex.execute(run);
You can also use
new Thread(run).start();
But ExecutorService is useful when we are using large number of thread in program.. have a look at this post

JavaFX: Task will not update UI

I'm working on an Omaha online poker client written in javaFX+java.
I need to show an anchorPane containing 3 buttons after control.call() finishes executing. I know for a fact that control.call() finishes executing but for some reason task.setOnSucceeded(new EventHandler() {'s handle method does not update the User interface.
What am I doing wrong?
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
}
});
new Thread(task).start();
}
The problem is that you are updating the user interface in the other thread.. if you are in the other thread and you want to update the user interface
You need to call the
Platform.runLater(new Runnable() {
#Override public void run() {
//Update UI here
}
});
calling this will call the main thread and update all the necessary update to the user interface
EDIT
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
});
}
}
});
new Thread(task).start();
}
As you override the call() method, you can override the succeeded() method: (See last example in the javadoc of Task.)
#Override protected void succeeded() {
super.succeeded();
updateMessage("Done!");
}
succeeded() is already called on the JavaFX Application Thread, so you do not need to do this for yourself.
Are you sure, that the code in your handle method isn't called? Maybe a NullPointerException is thrown, which you might not see? Is the comparison working as expected?
Try moving the code with if (client.getAction()... into the overridden succeeded() method and put a println before the if-statement in order to see whether it is called or not.
(EDIT: typos)
Your information has been helpful, but i think all methods suggested would have worked (including my initial one). The problem is that the task was never ended that's why the onTaskSucceded was never executed. In order to have the task exit after it has finished, i had to se the daemon to true:
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();

Running UI thread indefinitely

I am writing a piece of code using Java Swing. Basically what it does is that it processes some lengthy task. While the task is running, I want to have a waiting pop-up window with a GIF image in it.
My question is that
final InfoDialog infoDialog = new InfoDialog("Parsing file: " + fileToBeUploaded.getName());
final File finalFileToBeUploaded = fileToBeUploaded;
class FileParsingWorker extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
String text = fileParsers.parseFile(finalFileToBeUploaded);
publish(text);
return null;
}
#Override
protected void process(List<String> chunks) {
infoDialog.setVisible(false);
}
}
infoDialog.setVisible(true);
FileParsingWorker fileParsingWorker = new FileParsingWorker();
fileParsingWorker.execute();
The InfoDialog is the small UI pop-up window with a GIF animation in it. Basically, I put the lengthy task in the worker but the UI's setVisibles in two places. I am thinking if there is any ways I can run the InfoDialog UI in a thread so that I can reuse that bit of code?
The problem I have is that I want to try to run the InfoDialog indefinitely until I deliberately stop it. If I put setVisible(true) in a thread, that thread immediately terminates and my UI won't be updated.
Can someone show me how to do this?
Please have a read on Concurrency in Swing specifically The Event Dispatch Thread. This is the thread on which all Swing components should be created and manipulated. i.e:
SwingUtilities.invokeLater(new Runnable () {
#Override
public void run() {
final InfoDialog infoDialog = new InfoDialog("Parsing file: " + fileToBeUploaded.getName());
final File finalFileToBeUploaded = fileToBeUploaded;
...
infoDialog.setVisible(true);
FileParsingWorker fileParsingWorker = new FileParsingWorker();
fileParsingWorker.execute();
}
});
Also I think another problem is you set the dialog back to invisible in overriden process(List<String> chunks) of the Swing worker, thus as the first chunk is read the dialog will be closed. I think Swing Workers done() method might be more what you want, and its executed on EDT:
class FileParsingWorker extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
String text = fileParsers.parseFile(finalFileToBeUploaded);
publish(text);
return null;
}
#Override
protected void process(List<String> chunks) {
//each chunk will get processed here
}
#Override
protected void done() {//when Swing worker is finished this method is called
infoDialog.setVisible(false);
}
}

Waiting for thread while updating Swing

I have problem with handling threads in my application. It creates JFrame and starts a new Thread. Last one will execute external application and update GUI. Then
I have problem to make Main class to wait for second thread to finish, but also to update GUI simultaneously.
Here's my example (shortened):
class Main {
public int status;
public Main() {
// Creating GUI etc.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JDialog id = new JDialog();
id.button.addMouseListener(new MouseListener()); // Calls generate() method
}
});
}
public void generate() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Make changes to GUI
}
});
GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1);
genTest.start();
//while (status == 0);
System.out.println("Next step.");
}
}
And Thread class:
public class GeneratorThread extends Thread {
protected Main main;
protected int setSize, minValue, maxValue;
public GeneratorThread(Main main, int setSize, int minValue, int maxValue) {
this.main = main;
this.setSize = setSize;
this.minValue = minValue;
this.maxValue = maxValue;
}
public void run() {
// Execute program etc.
// Change GUI from main in the same time
// About 3 seconds
main.status = 1;
}
}
I'm in progress and I wanted to check how it works so far. While worked nice, but it locks Swing somehow and any changes are visible only when GeneratorThread finishes. I would like to update GUI in the real time.
I've tried join(), effects are the same. I also tried wait() (on Main), but then I got IllegalStateMonitorException.
Any hints?
Swing is a single threaded environment. That is, there is a single thread responsible for managing all the interactions and updates to the Swing UI - the Event Dispatching Thread.
Among the golden rules of Swing are...
DON'T block the EDT (Thread.sleep, Thread#join, Object#wait, block IO and/or time consuming tasks (among others) should never be called from within the EDT), doing so will stop the EDT from dispatching events and paint updates (amongst other things)
ONLY create/update Swing UI elements from within the EDT.
This raises a question...how do you "wait" for a thread?
The best way is use an Observer pattern. Basically, you provide the Thread with some kind of reference that it will call to provide notification of events, such as errors and completion...
This will require you to think very carefully about the design of your applications, as you can not rely on a simple A to B execution of your code.
For example...
public class TestThreadCallBack {
public static void main(String[] args) {
new TestThreadCallBack();
}
public TestThreadCallBack() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ThreadCallBack {
public void threadCompleted(Runnable source);
public void threadFailed(Runnable source);
}
public class TestPane extends JPanel implements ThreadCallBack {
private JLabel message;
private JLabel dots;
private int count;
private Timer timer;
public TestPane() {
setLayout(new GridBagLayout());
message = new JLabel("Running background task, please wait");
dots = new JLabel(" ");
add(message);
add(dots);
timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (count > 3) {
count = 0;
}
StringBuilder sb = new StringBuilder(3);
for (int index = 0; index < count; index++) {
sb.append(".");
}
for (int index = count; index < 3; index++) {
sb.append(" ");
}
dots.setText(sb.toString());
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
Thread thread = new Thread(new BackgroundTask(this));
thread.start();
}
#Override
public void threadCompleted(Runnable source) {
timer.stop();
message.setText("Task completed successfully");
}
#Override
public void threadFailed(Runnable source) {
timer.stop();
message.setText("Task failed");
}
}
public class BackgroundTask implements Runnable {
private ThreadCallBack callBack;
public BackgroundTask(ThreadCallBack callBack) {
this.callBack = callBack;
}
#Override
public void run() {
System.out.println("Background task underway...");
try {
Thread.sleep(2000);
} catch (InterruptedException interruptedException) {
}
int result = (int) Math.round((Math.random() * 1));
if (result == 0) {
callBack.threadCompleted(this);
} else {
callBack.threadFailed(this);
}
}
}
}
Updating the UI from within a Thread other then the EDT is, well, messy. An easier solution would actually be to use a SwingWorker. This has publish/process methods that make easy to update the UI and progress methods that can be used to provide feedback about the progress of the current task.
You can use it's done method to notify interested parties when the worker has completed.
Update your GUI from within the thread using SwingUtilitied.invokeLater or, alternatively, synchronise the main variable!
http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava
Maybe it already suffices to make "status" volatile?

Categories

Resources