How do I read a SwingWorker's result *without* busy wait? - java

I'm writing an application that executes its file menu actions using SwingWorker. Every called method returns a boolean value that tells, whether the operation was successfully executed or not.
At the moment I'm using busy waiting for the result, like this:
public boolean executeOperation() {
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
// ..
if (aborted) {
return false;
}
// ..
return true;
}
};
worker.execute();
// busy wait
while (!worker.isDone())
;
try {
return worker.get().booleanValue();
} catch (Exception e) {
// handle exceptions ..
return false;
}
}
Is there a less polling-intense way of solving this?
Using worker.get() directly wouldn't work, as it blocks the EDT, waiting for the task to finish - meaning even the dialogs I open from within the SwingWorker wouldn't get painted.
EDIT: If possible, I would like to avoid that the method (or the worker) to communicate their result asynchronously. I'm implementing several short methods (file -> open, new, close, save, save as, exit) that rely on each other (i. e. when the trying to exit, exit calls close, close might call save, save might call save as). Solving this asynchronously seems to make the code much more complicated.

The point of the SwingWorker is precisely to launch some task in the background and don't block the EDT. Either you want something synchronous, and the EDT will be blocked whatever you try, or you want something asynchronous, and the background task should update its status using the publish method of the SwingWorker.
You could display a blocking modal dialog with a progress bar while the task is running, and hide it once the task completes.
The alternative is to block for some time, hoping the task will be quick to finish, and then backup to an asynchronous way of doing. This can be done using the get method taking a timeout as argument.

You could use an asynchronous paradigm. Look at Observer / Observable and use the job to transfer the result back to the object which is currently doing the polling.

Using worker.get() directly wouldn't work, as it blocks the EDT, waiting for the task to finish - meaning even the dialogs I open from within the SwingWorker wouldn't get painted.
They don't with the current code either. Your busy wait blocks the EDT as much as calling worker.get() does - there is only one event dispatch thread, and the dialogs in the SwingWorker are just as blocked if that thread is spinning in a loop or awaiting a lock. The problem here is that if a method runs on the EDT, it simply can't return a value from an asynchronous operation (without hogging the EDT) to its caller.
The correct way to react to completed async processing is overriding the done() method in SwingWorker.
Also check out http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html for more info.

One way as mentioned by several folks above is to override the SwingWorker's done method. However if for some reason you want the post SwingWorker code outside of the SwingWorker and in the calling code, you can take advantage of SwingWorker's property change support. Simply add a PropertyChangeListener to the SwingWorker and listen for the state property which has a property name of "state". You can then extract the SwingWorker's state with its getState() method. When it is done it will return the DONE value of the SwingWorker.StateValue enum. For example (from an answer I've given in another thread here on SO):
if (turn == white) {
try {
final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() {
#Override
protected Move doInBackground() throws Exception {
Engine e = new Engine(); // Engine is implemented by runnable
e.start();
Move m = e.getBestMove(board);
return m;
}
};
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (StateValue.DONE == mySwingWorker.getState()) {
try {
Move m = mySwingWorker.get();
// TODO: insert code to run on the EDT after move determined
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
mySwingWorker.execute();
} catch (Exception e) {
e.printStackTrace();
}
}

I ran into a similar problem when I wanted a function to return a value that would be calculated in a swing worker. I didn't want to simply get that thread to block the EDT. I also didn't want it to block. So I used a semaphore like this:
public boolean executeOperation() {
final Semaphore semaphore = new Semaphore(1);
semaphore.acquire(1); // surround by try catch...
final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
// ..
if (aborted) {
semaphore.release();
return false;
}
// ..
semaphore.release();
return true;
}
};
worker.execute();
try {
semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up.
return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes.
} catch (Exception e) {
// handle exceptions ..
return false;
}
}
I guess this is not ideal for all situations. But I thought it was an alternative approach that was very useful for me.

Related

SwingWorker done method throws cancellationexception with get()

I faced an issue of creating stop/start jbuttons for my gui, and after a LOT of googling, i realized i needed multi-threading. Upon further reading i discovered the swingworker class, and i managed to get my GUI to respond to the STOP button.
now my problem is this
The doinbackground() method executes a piece of code that captures packets in an infinite while loop with the condition (!isCancelled), and once it is cancelled (The STOP button executes worker.cancel()) it returns an ArrayList of packets which theoretically, i should be able to obtain inside the done() method using get(). right? But when i try to do this i get a CancellationException and this is driving me nuts right now.
any help would be highly appreaciated!
Thank you
edit: obj is an ArrayList declared outside of the class to store the return values.
here is my code executed by the START jbutton
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
final ArrayList packet_list = new ArrayList();
obj.clear();
try {
worker = new SwingWorker<ArrayList,Integer>(){//initialze swingworker class
#Override
protected void done(){
try {
obj = get();
}
catch (InterruptedException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
//opens up stuff required to capture the packets
NetworkInterface [] devices = JpcapCaptor.getDeviceList();
int index = (jComboBox5.getSelectedIndex()-1);
JpcapCaptor captor =JpcapCaptor.openDevice(devices[4], 65535, false, 20);
#Override
protected ArrayList doInBackground(){
while(!isCancelled()){
try {
Packet packets = captor.getPacket(); //captures packets
if (packets != null) //filters out null packets
{
//System.out.println(packets);
packet_list.add(packets); //adds each packet to ArrayList
}
Thread.sleep(100);
} catch (InterruptedException ex) {
return packet_list;
}
}
return packet_list;
}
};
worker.execute();
} catch (IOException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
The stop button simply executes
worker.cancel(); no errors there. and this is the swingworker declaration
private SwingWorker<ArrayList,Integer> worker;
cancel doesn't just set the isCancelled flag for you to read at your leisure. That would be pretty much useless. It prevents the task from starting if it hasn't already and may actively interrupt the thread if it's already running. As such, getting a CancellationException is the natural consequence of cancelling a running task.
To further the point, the Javadoc on isCancelled states:
Returns true if this task was cancelled before it completed normally.
Hence if this returns true, then your task cannot complete normally. You cannot cancel a task and expect it to continue as per normal.
SwingWorker docs say "An abstract class to perform lengthy GUI-interaction tasks in a background thread". However, the definition of "lengthly" is different for GUI and for an application lifetime. A 100ms task is very long for a GUI, and is best done by a SwingWorker. A 10 minute task is too long for a SwingWorker simply because it has a limited thread pool, that you may exhaust. Judging by your problem description, you have exactly that - a potentially very long running task. As such, you should rather make a proper background thread than use a SwingWorker.
In that thread, you would have either an AtomicBoolean or simply a volatile boolean flag that you can manually set from the EDT. The thread can then post an event to the EDT with the result.
Code:
class PacketCaptureWorker implements Runnable {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
public void run() {
while (!cancelled) {
//do work
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Use the result of your computation on the EDT
}
});
}
}
new Thread(new PacketCaptureWorker()).start();
I tried using a volatile boolean instead of using worker.cancel() for the swingworker thread while loop and it works beautifully. (atleast on surface) I managed to create a normal background thread as well and that too worked liked a charm :D Many thanks you saved me a major headache! Wondering what the best method is out of the two.
A follow up, i had to make the volatile boolean available for the whole class, because i had to create 2 seperate instances for the thread class, one to use the START and the other to use the STOP. Apparently two different instances does not address the same instance of the variable. is this bad practice?

Clever asynchronous repaint in Java

I have a use-case coming from a GUI problem I would like to submit to your sagacity.
Use case
I have a GUI that displays a computation result depending on some parameters the user set in a GUI. For instance, when the user moves a slider, several events are fired, that all trigger a new computation. When the user adjust the slider value from A to B, a dozens of events are fired.
But the computation can take up to several seconds, whereas the slider adjustment can fire an event every few 100 ms.
How to write a proper Thread that would listen to these events, and kind of filter them so that the repaint of the results is lively? Ideally you would like something like
start a new computation as soon as first change event is received;
cancel the first computation if a new event is received, and start a new one with the new parameters;
but ensure that the last event will not be lost, because the last completed computation needs to be the one with last updated parameters.
What I have tried
A friend of mine (A. Cardona) proposed this low level approach of an Updater thread that prevents too many events to trigger a computation. I copy-paste it here (GPL):
He puts this in a class that extends Thread:
public void doUpdate() {
if (isInterrupted())
return;
synchronized (this) {
request++;
notify();
}
}
public void quit() {
interrupt();
synchronized (this) {
notify();
}
}
public void run() {
while (!isInterrupted()) {
try {
final long r;
synchronized (this) {
r = request;
}
// Call refreshable update from this thread
if (r > 0)
refresh(); // Will trigger re-computation
synchronized (this) {
if (r == request) {
request = 0; // reset
wait();
}
// else loop through to update again
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void refresh() {
// Execute computation and paint it
...
}
Every-time an event is sent by the GUI stating that parameters have been changed, we call updater.doUpdate(). This causes the method refresh() to be called much less.
But I have no control on this.
Another way?
I was wondering if there is another way to do that, that would use the jaca.concurrent classes. But I could not sort in the Executors framework what would be the one I should start with.
Does any of you have some experience with a similar use case?
Thanks
If you're using Swing, the SwingWorker provides capabilities for this, and you don't have to deal with the thread pool yourself.
Fire off a SwingWorker for each request. If a new request comes in and the worker is not done, you can cancel() it, and just start a new SwingWorker. Regarding what the other poster said, I don't think publish() and process() are what you are looking for (although they are also very useful), since they are meant for a case where the worker might fire off events faster than the GUI can process it.
ThingyWorker worker;
public void actionPerformed(ActionEvent e) {
if( worker != null ) worker.cancel();
worker = new ThingyWorker();
worker.execute();
}
class ThingyWorker extends SwingWorker<YOURCLASS, Object> {
#Override protected YOURCLASS doInBackground() throws Exception {
return doSomeComputation(); // Should be interruptible
}
#Override protected void done() {
worker = null; // Reset the reference to worker
YOURCLASS data;
try {
data = get();
} catch (Exception e) {
// May be InterruptedException or ExecutionException
e.printStackTrace();
return;
}
// Do something with data
}
}
Both the action and the done() method are executed on the same thread, so they can effectively check the reference to whether there is an existing worker.
Note that effectively this is doing the same thing that allows a GUI to cancel an existing operation, except the cancel is done automatically when a new request is fired.
I would provide a further degree of disconnect between the GUI and the controls by using a queue.
If you use a BlockingQueue between the two processes. Whenever the controls change you can post the new settings to the queue.
Your graphics component can read the queue whenever it likes and act on the arriving events or discard them as necessary.
I would look into SwingWorker.publish() (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html)
Publish allows the background thread of a SwingWorker object to cause calls to the process() method, but not every publish() call results in a process() call. If multiple process calls are made before process() returns and can be called again, SwingWorker concatenates the parameters used for multiple publish calls into one call to process.
I had a progress dialog which displayed files being processed; the files were processed faster than the UI could keep up with them, and I didn't want the processing to slow down to display the file names; I used this and had process display only the final filename sent to process(); all I wanted in this case was to indicate to the user where the current processing was, they weren't going to read all the filenames anyway. My UI worked very smoothly with this.
Take a look at the implementation of javax.swing.SwingWorker (source code in the Java JDK),
with a focus on the handshaking between two methods: publish and process.
These won't be directly applicable, as-is, to your problem - however they demonstrate how you might queue (publish) updates to a worker thread and then service them in your worker thread (process).
Since you only need the last work request, you don't even need a queue for your situation: keep only the last work request. Sample that "last request" over some small period (1 second), to avoid stopping/restarting many many times every 1 second, and if it's changed THEN stop the work and restart.
The reason you don't want to use publish / process as-is is that process always runs on the Swing Event Dispatch Thread - not at all suitable for long running calculations.
The key here is that you want to be able to cancel an ongoing computation. The computation must frequently check a condition to see if it needs to abort.
volatile Param newParam;
Result compute(Param param)
{
loop
compute a small sub problem
if(newParam!=null) // abort
return null;
return result
}
To handover param from event thread to compute thread
synchronized void put(Param param) // invoked by event thread
newParam = param;
notify();
synchronized Param take()
while(newParam==null)
wait();
Param param = newParam;
newParam=null;
return param;
And the compute thread does
public void run()
while(true)
Param param = take();
Result result = compute(param);
if(result!=null)
paint result in event thread

Learning Threads - Running a method only after the other has finished

In my button execution, I am calling 2 methods.
plotButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
startPrinterListOperation();
showAplotPlotterDialog();
}
});
The startPrinterListOperation(); takes some time to complete its task. But I do not want the method showAplotPlotterDialog(); to run until the first one has completed. So I am trying to use thread management to achieve this. Here is what I have tried.
plotButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Thread t = new Thread() {
public void run() {
startPrinterListOperation();
}
};
t.start();
try {
t.join();
}
catch (InterruptedException e1) {
e1.printStackTrace();
}
showAplotPlotterDialog();
}
});
But the second method stills starts before the first one has finished.
Extending on my comment: Seems like startPrinterListOperation launches an asynchronous operation and finishes instantly, evidented by the join succeeding.
If the launched async op is out of your control, then you might be able to observe it finishing via some callback, polling, etc. Then you may employ something like the following in startPrinterListOperation:
void startPrinterListOperation() {
final CountDownLatch c1 = new CountDownLatch(1);
launchTheAsyncOp(new SomeCallback() {
void x() {
c1.countDown();
}
});
try {
c1.await(999, TimeUnit.SECONDS)
}
catch (InterruptedException e) {
throw new MyRuntimeException("user didn't finish the op in 999 seconds, fail");
}
}
I would not bother with threads, this will just make your program overly complicated.
Can you edit the startPrinterListOperation() method?
I would instead add showAplotPlotterDialog(); to the end of the startPrinter method, and the last last thing the method does.
Answering your general question in the title, you have a master thread that calls your two methods directly, so that the second method waits for the first method to complete.
I understand that in your specific case, the first method runs for a while, and you would prefer that the user not have to wait.
You should call a generatePrinterList() method in a separate thread while you're constructing the GUI. You do this because your GUI users are very likely to print or plot, and the printer list is not likely to change while the user is using your GUI.
Odds are that the generatePrinterList() thread will finish long before your user has to print or plot. But just to be sure, the thread has to have a way of reporting back that the thread is completed. I use a boolean isCompleted that can be read with a public isCompleted() method.
The isCompleted() method could have a thread sleep loop if you want, so it always returns true. In this case the method doesn't have to return anything.

Java Swing Threading with Updatable JProgressBar

First off I've been working with Java's Concurrency package quite a bit lately but I have found an issue that I am stuck on. I want to have and Application and the Application can have a SplashScreen with a status bar and the loading of other data. So I decided to use SwingUtilities.invokeAndWait( call the splash component here ). The SplashScreen then appears with a JProgressBar and runs a group of threads. But I can't seem to get a good handle on things. I've looked over SwingWorker and tried using it for this purpose but the thread just returns. Here is a bit of pseudo code. and the points I'm trying to achieve.
Have an Application that has a SplashScreen that pauses while loading info
Be able to run multiple threads under the SplashScreen
Have the progress bar of the SplashScreen Update-able yet not exit until all threads are done.
Launching splash screen
try {
SwingUtilities.invokeAndWait( SplashScreen );
} catch (InterruptedException e) {
} catch (InvocationTargetException e) { }
Splash screen construction
SplashScreen extends JFrame implements Runnable{
public void run() {
//run threads
//while updating status bar
}
}
I have tried many things including SwingWorkers, Threads using CountDownLatch's, and others. The CountDownLatch's actually worked in the manner I wanted to do the processing but I was unable to update the GUI. When using the SwingWorkers either the invokeAndWait was basically nullified (which is their purpose) or it wouldn't update the GUI still even when using a PropertyChangedListener. If someone else has a couple ideas it would be great to hear them. Thanks in advance.
I actually got ready to post better code to help out and found my solution. I thank you for all who helped.
For running a series of operations in the background and reporting progress, use SwingWorker.
The background method does the background processing.
Use the publish method to post periodic status updates.
Override the process method to handle the updates (process always executes on the EDT).
progressBar = new JProgressBar();
sw = new SwingWorker<Boolean,Integer>() {
protected Boolean doInBackground() throws Exception {
// If any of the operations fail, return false to notify done()
// Do thing 1
publish(25); // 25% done
// Do thing 2
publish(50); // 50% done
// Do thing 3
publish(75); // 75% done
// Do thing 4
return true;
}
protected void process(List<Integer> chunks) {
for (Integer i : chunks)
progressBar.setValue(i);
}
protected void done() {
try {
boolean b = get();
if (b)
progressBar.setValue(100); // 100% done
else
// Notify the user processing failed
}
catch (InterruptedException ex) {
// Notify the user processing was interrupted
}
catch (ExecutionException ex) {
// Notify the user processing raised an exception
}
}
};
Addendum:
This can be extended to multiple tasks, it just requires changing how you approach setting the progress bar. Here's what comes to mind:
Have an array of completion counter, one per task.
int[] completions = new int[numTasks];
Arrays.fill(completions,0);
Start the SwingWorkers, each passed an index number. The process or done methods then call something like this to update the overall progress bar.
void update(int index, int percComplete) {
completions[index] = percComplete;
int total = 0;
for(int comp: completions)
total += comp/numTasks;
overallPB.setValue(total);
}
Optionally, display a JProgressBar per task.
Addendum 2:
If the tasks vary in completion time (eg, cache hit vs cache miss), you may want to investigate ProgressMonitor. It's a progress dialog that only appears if the task takes more than some (configurable, default 500ms) amount of time.
No need to call the frame inside invokeAndWait but you should update progress bar state like this.
try {
SwingUtilities.invokeAndWait( new Runnable() {
public void run() {
//update state of the progress bar here
}
});
} catch (InterruptedException e) {
} catch (InvocationTargetException e) { }

Waiting for invokeLater() to be called

Is there a more elegant way to do what I'm doing below? That is, is there a more elegant way than polling and sleeping, polling and sleeping, and so on to know when a Runnable.run() method has been called via invokeLater()?
private int myMethod() {
final WaitForEventQueue waitForQueue = new WaitForEventQueue();
EventQueue.invokeLater(waitForQueue);
while (!waitForQueue.done) {
try {
Thread.sleep(10);
} catch (InterruptedException ignore) {
}
}
return 0;
}
private class WaitForEventQueue implements Runnable {
private boolean done;
public void run() {
// Let all Swing text stuff finish.
done = true;
}
}
A better way would be to use a FutureTask (which implements Runnable and Future) and override its done() event to do something when finished.
Also, start this as a separate thread (or use an Executor) if it's not doing GUI manipulation, rather than using the AWT EventQueue.
If you want to wait, why not call invokeAndWait rather than implement it yourself?
Instead of waiting for the thread to finish, why not just have the UI display a spinner or something, and have the thread call an event when it is done.

Categories