Is invoking SwingWorker run a mistake and how to synchronize doInBackground? - java

I'm using a SwingWorker as a way to wrap non swing tasks/actions with a before and after methods always running in the Event Dispatch Thread (in other words: I invoke before then I invoke an abstract method in the doInBackground and then I invoke after in the done method). This started causing a lot of trouble lately since whatever happens in doInBackground has to be synchronized. I noticed that the problem goes away if I call run instead of execute, but from what I understand the run method works the same as in Thread, it just starts the execution in the exact same thread it has been called from instead of creating a new one, and as such the doInBackground executes on the EDT if I call run from the EDT. Is my thinking correct? How can I synchronize the doInBackground method with the thread calling execute on the worker?
I used the following code but it seems to be creating a thread lock:
private Object LOCK = new Object();
public final void method() {
someObject.before();
SwingWorker<Void, Void> worker1 = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
methodExt();
return null;
}
protected void done() {
someObject.after();
synchronized (LOCK) {
LOCK.notifyAll();
}
}
};
worker1.execute();
synchronized (LOCK) {
try {
LOCK.wait();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}

You should never block on the EDT. The whole point of SwingWorker is that it allows you to trigger a task to run on a background thread and schedule additional code to run on the EDT after the background job is complete without blocking the EDT in the meantime. So if you want to run methodA() on the EDT, then methodB() in the background, then methodC() back on the EDT after B has completed, the answer is to call methodC() from the done() method of the SwingWorker.
Instead of trying to do
doSomething();
method(); // which blocks waiting for the BG task to complete
doSomethingElse();
you could modify method() to
public final void method(final Runnable callback) {
someObject.before();
SwingWorker<Void, Void> worker1 = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
methodExt();
return null;
}
protected void done() {
someObject.after();
if(callback != null) callback.run();
}
};
worker1.execute();
}
and then call it as
doSomething();
method(new Runnable() {
public void run() {
doSomethingElse();
}
});
Now method returns immediately and does not block the EDT, but doSomethingElse still happens on the EDT after the background task is complete.

Related

Any caveats to a runnable storing a reference to its own running thread?

I have a long running Runnable object and I wanted to provide a more graceful interrupt mechanism than having to call interrupt on the thread the object is running on.
The before code:
public class MyRunnable implements Runnable {
public void run() {
while(!Thread.currentThread().isInterrupted()) {
//do stuff
}
}
}
public class MyClass {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable, "myRunnableThread");
t.start();
//do stuff
t.interrupt();
//do stuff
}
}
And the new code:
public class MyRunnable implements Runnable {
private Thread myThread = null;
public void run() {
myThread = Thread.currentThread();
while(!myThread.isInterrupted()) {
//do stuff
}
}
public void shutdown() {
if (myThread != null) {
myThread.interrupt();
//do other shutdown stuff
}
}
}
public class MyClass {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable, "myRunnableThread");
t.start();
//do stuff
myRunnable.shutdown();
//do stuff
}
}
My question is, are there possible side effects or unknowns that holding a reference to your own thread, and providing limited access to that thread through public methods (as above) could cause? This is assuming that no-one ever calls the run() method directly, that it is always started from a new thread.
And I'm aware that I could use a volatile or atomic Boolean in the run() and shutdown() methods for communicating intent to shutdown, I'm more interested in learning than a solution. But solutions are still welcome!
For me your first approach is much better as less error prone and more "standard". But actually what you try to implement already exists (which proves that it makes sense and that it is not a bad practice but it is not easy to make it properly), it is called FutureTask, instead of shutdown you have cancel(boolean mayInterruptIfRunning) with true as value of mayInterruptIfRunning if you want to interrupt the thread running the task, I quote the javadoc:
Attempts to cancel execution of this task. This attempt will fail if
the task has already completed, has already been cancelled, or could
not be cancelled for some other reason. If successful, and this task
has not started when cancel is called, this task should never run. If
the task has already started, then the mayInterruptIfRunning
parameter determines whether the thread executing this task should be
interrupted in an attempt to stop the task.
For example:
// Task that will only sleep for 1 sec and print a message on interrupted
FutureTask<Void> myRunnable = new FutureTask<>(
new Callable<Void>() {
#Override
public Void call() throws Exception {
try {
System.out.println("Sleep");
Thread.sleep(1_000L);
} catch (InterruptedException e) {
System.out.println("Interrupted !!!");
throw e;
}
return null;
}
}
);
new Thread(myRunnable, "myRunnableThread").start();
// Wait long enough to make sure that myRunnableThread is sleeping
Thread.sleep(500L);
// Cancel the task and interrupt myRunnableThread
myRunnable.cancel(true);
Output:
Sleep
Interrupted !!!
It already has a reference:
Thread.currentThread()
From the javadoc:
Returns a reference to the currently executing thread object.

ScheduledThreadPoolExecutor only runs Swingworker once

ScheduledThreadPoolExecutor (which implements ScheduledExecutorService) seems to be only running the SwingWorker class once when using the ScheduleAtFixedRate method. The original code is kinda long, so I made a new code that produces the same results below.
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class ScheduledThreadPoolExecutorTest extends SwingWorker<Void, Void>{
#Override
protected Void doInBackground() {
System.out.println("Yay!");
return null;
}
#Override
protected void done() {
try {
get();
} catch(Exception e) {
e.printStackTrace();
}
System.out.println("Woohoo!");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.scheduleAtFixedRate(new ScheduledThreadPoolExecutorTest(), 0, 30, TimeUnit.MILLISECONDS);
}
});
}
}
This yields the results:
Yay!
Woohoo!
Why is ScheduledThreadPoolExecutor running SwingWorker only once? And what can I do to make the SwingWorker run every 30 milliseconds as indicated in the code?
While SwingWorker does implement the Runnable interface, per its API section on the doInBackground() method:
Note that this method is executed only once.
So while its inner run() method may repeatedly run, the doInBackground() will only run once. Not only that, but the run() method is marked final within SwingWorker, and so you can't override it to call doInBackground multiple times.
A better solution is not use a SwingWorker at all but rather a simpler Runnable-derived class.
SwingWorker extends Runnable, however, it uses FutureTask to run its computation.
From the javadoc:
A cancellable asynchronous computation. This class provides a base
implementation of {#link Future}, with methods to start and cancel
a computation, query to see if the computation is complete, and
retrieve the result of the computation. The result can only be
retrieved when the computation has completed; the {#code get}
methods will block if the computation has not yet completed. Once
the computation has completed, the computation cannot be restarted
or cancelled (unless the computation is invoked using
{#link #runAndReset}).
That is, the FutureTask will run only once, if you try to run it again, it will simply return.
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

Stop the thread started within a object after the object is no longer in a list?

Say I have a class call MyTask
Every time I new a object for MyTask, it will create a thread
boolean mContinueThread = true;
public MyTask (Activity activity) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (mContinueThread) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
});
thread.start();
return null;
};
}.execute();
}
At first I new myTask1, myTask2, myTask3 then add to the List
List<MyTask> myTasks = new ArrayList<MyTask>;
myTasks.add(myTask1);
myTasks.add(myTask2);
myTasks.add(myTask3);
Now there should be 3 threads run on the background, then I renew the by
myTasks = new ArrayList<MyTask>;
And looks like those threads in myTask1, myTask2, myTask3 are still running, the finalize never get called. I know I can set mContinueThread as true for each MyTask objects before I renew the list, but I would like to know is there any way (callback?) I can know those MyTask objects are not no longer in the list and then set mContinueThread as false?
public MyTask (Activity activity) {
...
#Override
protected void finalize() throws Throwable {
mContinueThread = false;
super.finalize();
}
It seems redundant to have an async task which just starts a Thread.You can achieve the desired outcome, by puting the contance of the thread directly into you AsyncTask$doInBackground()
You can call the call the AsyncTask$cancel(boolean mayInterrupt) method, this will rise an InterruptedException, the only thing left to do, is adding a return statement within the catch:
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// cancel was called
return null;
}
return null;
};
Cancel the task like that:
myTasks.get(i).cancel(true);
Don't forget to pass true or it won't work
you have to have a variable for your AsyncTask first, so that you can call:
myTasks.get(0).getMyAsyncTask().cancel(boolean);
As you can see here, it is not that easy to cancel an AsyncTask. After you call cancel(boolean), it will: (from docs:
invoking this method will cause subsequent call to isCancelled() to
return true. onCancelled(Object) will be invoked after doInBackground
instead of onPostxecute. To ensure that a task is cancelled, you
should always check the return value of isCancelled from
doInBackground.
So, you call cancel(booelan) onto your reference variable of your AsyncTask and in doInBackground, you always check for isCancelled() in order to stop some processes in your doInBackground method.
You should handle your doInBackground method manually in order to stop all the executions there. So if you have while(true){ do_something }, then it should become while(true){ if(!isCancelled()) { do_something } }

Waiting for all Runnables submitted to SWT UI thread with Display::asyncExec() to finish

Is there a way to wait for all Runnables submitted to the SWT UI Thread via asyncExec(...) to finish?
Background:
I have a long-running operation, which among other things is triggering events that in turn submit Runnables to the SWT UI thread via the asyncExec(...) instance method of Display.
The progress of the long-running operation is shown in a ProgressMonitorDialog, and I would like to close the dialog only after the UI thread has finished executing the Runnables.
Changing the calls from asyncExec(...) to syncExec(...) is not an option, as the latter is not desired when the events are triggered from other contexts.
org.eclipse.swt.widgets.Display.readAndDispatch() will process an event from the event queue and return false if there are no more events to process. But you probably don't want to use this as it processes an event.
asyncExec(*) is a FIFO queue (although OS graphics events supersede the asyncExecs), so you could do most of your long-running op processing and then place a final asyncExec in the queue:
final boolean[] done = new boolean[1];
Runnable r = new Runnable() {
public void run() {
done[0] = true;
}
};
// now wait for the event somehow. The brute force method:
while (!done[0]) {
Thread.sleep(200);
}
In theory, all of the other asyncExecs spawned from your long running op will be finished by the time you get to the last one.
EDIT: potential other option
Create your own org.eclipse.core.runtime.jobs.Job and then join() it at the end:
public static class RefCountJob extends Job {
public RefCountJob() {
super("REF_COUNT");
}
int count = 0;
public void increment() {
count++;
}
public void decrement() {
count--;
}
#Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("WAITING", IProgressMonitor.UNKNOWN);
while (count > 0) {
Thread.sleep(200);
monitor.worked(1);
}
monitor.done();
return Status.OK_STATUS;
}
}
To use it, increment() it every time you are going to fire off events, and have them decrement it when they're done (You have to make sure they decrement it no matter what exception is thrown :-)
RefCountJob ref = new RefCountJob();
// ... do stuff, everybody increments and decrements ref
ref.increment();
// ... do more stuff
ref.increment();
// at the end of your long-running job
ref.schedule();
ref.join();
Thanks, I ended up with the following. I think it is a pretty clean solution. By the way I would upvote your answer if I had enough reputation for that :)
public class SWTThreadingUtils
{
public static void waitForAsyncExecsToFinish(Display display)
{
Object waitObj = new Object();
display.asyncExec(new DummyRunnable(waitObj));
synchronized (waitObj)
{
try {
waitObj.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private static class DummyRunnable implements Runnable
{
private Object waitObj;
public DummyRunnable(Object waitObj)
{
this.waitObj = waitObj;
}
#Override
public void run()
{
synchronized (waitObj)
{
waitObj.notify();
}
}
}
}

Graceful exception handling in Swing Worker

I am using threading in application through Swing Worker class. It works fine, yet I have a bad feeling about showing an error message dialog in try-catch block. Can it potentially block the application? This is what it looks right now:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
public Void doInBackground() {
try {
DoFancyStuff();
} catch (Exception e) {
e.printStackTrace();
String msg = String.format("Unexpected problem: %s", e
.toString());
//TODO: executed in background thread and should be executed in EDT?
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE,
errorIcon);
}//END: try-catch
return null;
}
// Executed in event dispatch thread
public void done() {
System.out.println("Done");
}
};
Can it be done in a safe way using Swing Worker framework? Is overriding publish() method a good lead here?
EDIT:
Did it like this:
} catch (final Exception e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
e.printStackTrace();
String msg = String.format(
"Unexpected problem: %s", e.toString());
JOptionPane.showMessageDialog(Utils
.getActiveFrame(), msg, "Error",
JOptionPane.ERROR_MESSAGE, errorIcon);
}
});
}
Calling get in done method would result in two try-catch blocks, as the computational part throws exceptions, so I think this is cleaner in the end.
The right way to do it is as follows:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// Executed in background thread
protected Void doInBackground() throws Exception {
DoFancyStuff();
return null;
}
// Executed in EDT
protected void done() {
try {
System.out.println("Done");
get();
} catch (ExecutionException e) {
e.getCause().printStackTrace();
String msg = String.format("Unexpected problem: %s",
e.getCause().toString());
JOptionPane.showMessageDialog(Utils.getActiveFrame(),
msg, "Error", JOptionPane.ERROR_MESSAGE, errorIcon);
} catch (InterruptedException e) {
// Process e here
}
}
}
You should NOT try to catch exceptions in the background thread but rather let them pass through to the SwingWorker itself, and then you can get them in the done() method by calling get()which normally returns the result of doInBackground() (Voidin your situation). If an exceptionwas thrown in the background thread then get() will throw it, wrapped inside an ExecutionException.
Please also note that overidden SwingWorker methods are protected and you don't need to make them public.
One option is to use SwingUtilities.invokeLater(...) to post the action on the EDT
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JOptionPane.showMessageDialog(
Utils.getActiveFrame(),
msg,
"Error",
JOptionPane.ERROR_MESSAGE,
errorIcon);
}
});
And as you noted, SwingWorker is capable of reporting intermediate results, but you'll need to override process(...), which is called when you invoke publish(...).
Regardless, why not just set a flag if an exception occurs, and if that flag is set, show the dialog in done() since it's executed safely in the EDT?
You are right, you are violating the cardinal rule of Swing, which is don't modify the GUI anywhere except for on the event-dispatch-thread.
If it was me, I would throw an event that the GUI listens for to show the error message. Or, you can just wrap the invocation of the SwingWorker in a try catch and show the dialogue there.
First of all: sorry for the short answer, don't have too much time to spare.
I had the same problem: wanting to publish to System.out from within the worker.
Short answer: It won't block your app if you use the execute() method
The thing is that there is no blocking if you execute the worker as it should be: a background task.
class MyWorker extend SwingWorker<Void, Void>{
#Override
protected Void doInBackground() throws ... {
// your logic here and a message to a stream
System.out.println("from my worker, with love");
// ...
try {
throw new Exception("Whoops, this is an exception from within the worker");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Now you will invoke this worker creating a new instance, and after that calling the execute() method. But to save you some time: you will probably want to know when your worker is done, so you'll need to register an property change listener, which is fairly simple:
class MyListener implements PropertyChangeListener{
#Override
public void propertyChange(PropertyChangeEvent evt){
if(evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)){
System.out.println("The worker is done");
}
}
}
And to put everything together at your main():
public void main(...){
MyWorker w = new MyWorker();
MyListener l = new MyListener();
w.addPropertyChangeListener(l);
w.execute();
}

Categories