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

时间:2017-07-10 08:08:00

标签: java multithreading multitasking callable

对于我在一个单独的线程中运行计算有点“初学者”的问题感到抱歉,但我是一名C ++程序员。

处理大图像的计算成本高昂。在处理过程中,我希望能够使用我的软件(包括缩放操作)。

基于您的advice(程序返回数据 - 新图像) 已使用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(); 

不幸的是,这种实现方式并不像我预期的那样。虽然它在一个单独的线程中工作,但“响应”不会返回到主窗口,我无法在计算期间正确使用该软件

因此,我尝试修改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 (); 

并且多任务处理按预期工作......

所以我的问题是:是否有必要另外“调整”或调整Callable接口,若然,怎么做?

2 个答案:

答案 0 :(得分:2)

BufferedImage img = res.get(); 

这将阻止调用它的整个线程,直到计算出图像。 我假设您从主要或UI消息调度线程调用此函数,因此这就是您的UI被阻止的原因。

有几种方法可以解决这个问题:

  • 实施通知机制,在完全计算图像时通知您的UI。你可以,例如将监听器传递给B的构造函数,存储它,并在计算结束时通知它。
  • 定期检查您的未来是否已完成(isDone()),然后执行操作。
  • 还有一些实用程序库提供有关Java Concurrency的通知基础结构。

编辑:请求举例:

如果不了解完整的应用程序,或者至少是技术堆栈,很难给出一个很好的例子。

为了避免实现自己的接口(我将在我的应用程序中执行),我将重用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班延长:

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服务......

答案 1 :(得分:2)

我建议您使用遗嘱执行人服务。

exe.submit(()->{
    BufferedImage img = res.get();
    uiRelatedMethod(img);
});

这样你的gui线程就不会阻塞,一旦缓冲的图像可用就会得到通知。当然你必须使用try / catch块。