可在单独的线程中调用与可运行和多任务处理

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 块。