Reputation: 4255
Sorry for my slightly "beginner" question related to the running of computations in a separate thread, but I am a C++ programmer.
There is a computationally expensive task processing large images. During the processing I would like to be able to work with my software (including zoom operations).
Based on your advice (the procedure returns data - a new image) the Callable interface has been used:
public class B implements Callable<BufferedImage> {
private boolean c;
public B (boolean c) { this.c = c; }
public BufferedImage call() {
//Some operations
if (!c)
return null;
return new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
}
}
Initially, the executor service is created:
ExecutorService exe = Executors.newFixedThreadPool(2);
B b = new B(true);
Subsequently, the future is returned:
Future<BufferedImage> res = exe.submit(b);
Finally, we are waiting for the data:
BufferedImage img = res.get();
Unfortunately, this implementation does not behave the way I expected. Although it works in a separate thread, the "response" is not returned to the main window and I am not able to work with the software properly during the computation.
Therefore, I tried to modify the get() method so that
try
{
BufferedImage img_proj = results.get(5, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
results.cancel(true);
e.printStackTrace();
}
However, the TimeoutException appears. Rewriting the code back using the Runnable interface
public class B implements Runnable{
private boolean c;
private Runnable f;
public B (boolean c_, Runnable f_) { c = c_; f = f_;}
public BufferedImage process() {
//Some operations
BufferedImage output = null;
if (c) output = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
return output;
}
public void run() { process();}
}
together with
Thread t = new Thread(b);
t.start ();
and the multitasking works as expected...
So my question is: Is it necessary to "tune" or tweak the Callable interface additionally and if so, how?
Upvotes: 0
Views: 340
Reputation: 2326
BufferedImage img = res.get();
This blocks the whole thread where it is called from until the image is calculated. I assume you call this from your main or UI message dispatch thread, so this is why your UI is blocked.
There are several approaches to solve this:
isDone()
), and perform actions then.EDIT: As an example was requested:
It is hard to give a good example without knowing your complete application, or at least its technology stack.
To avoid implementing an own interface (which I would do in my application), I will re-use Java's ChangeListener interface:
public void myButtonWasClicked() {
// all your stuff setting up executor...
// yes, this could be written much shorter with Java 8
ChangeListener myChangeListener = new ChangeListener() {
public void stateChanged(ChangeEvent evt) {
handleImageReady((BufferedImage)evt.getSource());
}
}
B b = new B(true, myChangeListener);
exe.submit(b);
}
Your B class extended:
public class B implements Callable<BufferedImage> {
private boolean c;
private ChangeListener listener;
public B (boolean c, ChangeListener listener) { this.c = c; this.listener = listener; }
public BufferedImage call() {
//Some operations
if (!c)
return null;
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
// pass finished image to listener who will handle in the UI
listener.stateChanged(new ChangeEvent(img));
return img; // as nobody will consume this any longer, you could as well switch back to Runnable instead of Callable...
}
}
Please note that this is a really rough example for a Java beginner. There would be many things to improve. For example, the Executor service must be shut down somewhere...
Upvotes: 2
Reputation: 12346
I would suggest using your executor service.
exe.submit(()->{
BufferedImage img = res.get();
uiRelatedMethod(img);
});
That way your gui thread doesn't block, and it will get notified once the buffered image is available. Of course you'll have to use a try/catch block.
Upvotes: 2