可在单独的线程中调用与可运行和多任务处理
Callable in a separate thread vs. Runable and mulitasking
很抱歉我的 "beginner" 问题与单独线程中的 运行 计算有关,但我是一名 C++ 程序员。
有一项处理大型图像的计算量大的任务。在处理过程中,我希望能够使用我的软件(包括缩放操作)。
根据你的(程序returns数据-新图像)
已使用 Callable 接口:
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);
}
}
最初,创建执行器服务:
ExecutorService exe = Executors.newFixedThreadPool(2);
B b = new B(true);
随后返回未来:
Future<BufferedImage> res = exe.submit(b);
终于等到数据了:
BufferedImage img = res.get();
不幸的是,这个实现并不像我预期的那样运行。虽然它在一个单独的线程中工作,但 "response" 不会返回到主 window 并且我无法在计算 期间正确使用该软件 。
因此,我尝试修改get()方法,使得
try
{
BufferedImage img_proj = results.get(5, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
results.cancel(true);
e.printStackTrace();
}
但是,出现TimeoutException。使用 Runnable 接口重写代码
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();}
}
连同
Thread t = new Thread(b);
t.start ();
并且多任务处理按预期工作...
所以我的问题是:是否有必要 "tune" 或另外调整 Callable 接口,如果需要,如何调整?
BufferedImage img = res.get();
这会阻塞调用它的整个线程,直到计算图像为止。
我假设您是从主线程或 UI 消息调度线程调用它的,所以这就是您的 UI 被阻止的原因。
有几种方法可以解决这个问题:
- 实施通知机制,当图像计算完成时通知您 UI。你可以例如将 Listener 传递给 B 的构造函数,存储它,并在计算结束时通知它。
- 定期检查您的期货是否已完成 (
isDone()
),然后执行操作。
- 还有一些实用程序库提供围绕 Java 并发性的通知基础结构。
编辑:要求举个例子:
如果不了解您的完整应用程序或至少其技术栈,很难给出一个好的示例。
为了避免实现自己的接口(我会在我的应用程序中这样做),我将重新使用 Java 的 ChangeListener 接口:
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);
}
你的 B class 扩展:
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...
}
}
请注意,对于 Java 初学者来说,这是一个非常粗略的示例。会有很多事情需要改进。比如Executor服务一定要在某处关闭...
我建议使用你们的执行服务。
exe.submit(()->{
BufferedImage img = res.get();
uiRelatedMethod(img);
});
这样您的 gui 线程就不会阻塞,一旦缓冲图像可用,它就会收到通知。当然,您必须使用 try/catch 块。
很抱歉我的 "beginner" 问题与单独线程中的 运行 计算有关,但我是一名 C++ 程序员。
有一项处理大型图像的计算量大的任务。在处理过程中,我希望能够使用我的软件(包括缩放操作)。
根据你的
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);
}
}
最初,创建执行器服务:
ExecutorService exe = Executors.newFixedThreadPool(2);
B b = new B(true);
随后返回未来:
Future<BufferedImage> res = exe.submit(b);
终于等到数据了:
BufferedImage img = res.get();
不幸的是,这个实现并不像我预期的那样运行。虽然它在一个单独的线程中工作,但 "response" 不会返回到主 window 并且我无法在计算 期间正确使用该软件 。
因此,我尝试修改get()方法,使得
try
{
BufferedImage img_proj = results.get(5, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
results.cancel(true);
e.printStackTrace();
}
但是,出现TimeoutException。使用 Runnable 接口重写代码
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();}
}
连同
Thread t = new Thread(b);
t.start ();
并且多任务处理按预期工作...
所以我的问题是:是否有必要 "tune" 或另外调整 Callable 接口,如果需要,如何调整?
BufferedImage img = res.get();
这会阻塞调用它的整个线程,直到计算图像为止。 我假设您是从主线程或 UI 消息调度线程调用它的,所以这就是您的 UI 被阻止的原因。
有几种方法可以解决这个问题:
- 实施通知机制,当图像计算完成时通知您 UI。你可以例如将 Listener 传递给 B 的构造函数,存储它,并在计算结束时通知它。
- 定期检查您的期货是否已完成 (
isDone()
),然后执行操作。 - 还有一些实用程序库提供围绕 Java 并发性的通知基础结构。
编辑:要求举个例子:
如果不了解您的完整应用程序或至少其技术栈,很难给出一个好的示例。
为了避免实现自己的接口(我会在我的应用程序中这样做),我将重新使用 Java 的 ChangeListener 接口:
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);
}
你的 B class 扩展:
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...
}
}
请注意,对于 Java 初学者来说,这是一个非常粗略的示例。会有很多事情需要改进。比如Executor服务一定要在某处关闭...
我建议使用你们的执行服务。
exe.submit(()->{
BufferedImage img = res.get();
uiRelatedMethod(img);
});
这样您的 gui 线程就不会阻塞,一旦缓冲图像可用,它就会收到通知。当然,您必须使用 try/catch 块。