【问题标题】:Callable in a separate thread vs. Runable and mulitasking可在单独的线程中调用与可运行和多任务处理
【发布时间】:2017-12-13 21:05:29
【问题描述】:

抱歉,我提出了与在单独线程中运行计算相关的“初学者”问题,但我是一名 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 接口?如果需要,如何调整?

【问题讨论】:

    标签: java multithreading multitasking callable


    【解决方案1】:
    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 类扩展:

    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 服务必须在某处关闭...

    【讨论】:

    • @Florian: 谢谢你的回答。我可以问一些例子吗?
    • @Florian: 非常感谢您的说明性示例,现在看起来很容易理解...
    【解决方案2】:

    我建议使用您的执行器服务。

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

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

    【讨论】:

    • @马特:谢谢。这里的 res 是什么意思?在我的代码中 Future res 代表提交过程的结果....
    • 是的,正是您示例中的 res。不只是调用 res.get(),而是将 res.get 作为一个任务提交给你的执行者。
    • @matt:效果很好...几乎没有对我当前的代码进行任何更改。谢谢。
    猜你喜欢
    • 2015-12-29
    • 1970-01-01
    • 2023-01-31
    • 2014-10-12
    • 1970-01-01
    • 2012-12-05
    • 2015-04-21
    • 1970-01-01
    • 2021-07-11
    相关资源
    最近更新 更多