【问题标题】:Why Future isDone() block the program like Future get()?为什么 Future isDone() 会像 Future get() 这样阻塞程序?
【发布时间】:2016-03-18 21:56:10
【问题描述】:

我正在编写一个生成分形和分形动画的程序,问题出在动画上...

我有一个生成List<Callable<Long>> tasks 的主线程,其中每个元素都有生成一帧的信息和资源;然后我使用ExecutorService 提交作品。

问题在于如果用户想要停止演算,则无法取消这些辅助线程。代码如下:

public class Animation extends Task<Long> {
protected Long call() throws Exception {
    long startTime = System.currentTimeMillis();

    WritableImage[] frames = new WritableImage[frameNumber];
    List<Callable<Long>> tasks = new ArrayList<>();

    updateProgress(count.incrementAndGet(), maxCount);
    if (isCancelled()) {
        return System.currentTimeMillis() - startTime;;
    }

    for (int k = 0; k < frameNumber; k++) {
        frames[k] = new WritableImage(
                (int) start.getCartesianPlane().getWidth(),
                (int) start.getCartesianPlane().getHeight());

        CartesianFractal tmp = FractalFactory.bulidFractal(
                selectedFractal, nextDataBox(k), colorPalette);

        tmp.setOnFinish(t -> {
            updateProgress(count.incrementAndGet(), maxCount);
            return null;
        });

        tasks.add((Callable<Long>) tmp);
        if (isCancelled()) {
            return System.currentTimeMillis() - startTime;;
        }
    }

    executor = Executors.newFixedThreadPool(4);
    updateProgress(count.incrementAndGet(), maxCount);
    if (isCancelled()) {
        return System.currentTimeMillis() - startTime;
    }
    try {
        result = executor.invokeAll(tasks);
    }
    catch (InterruptedException ex) {
        System.err.println(ex.toString());
    }

    // Check if all tasks are finished
    boolean finished = false;
    while (!finished) {
        finished = true;
        // Check if it is all done
        for (Future<Long> r : result) {
            finished = finished && r.isDone(); // THE PROGRAM BLOCKS HERE
            // Check if the task was cancelled
            if (isCancelled()) {
                // Cancell all task
                tasks.stream().forEach((t) -> {
                    ((CartesianFractal)t).myCancel();
                });
                // Turnoff the executor
                executor.shutdown();
                return System.currentTimeMillis() - startTime;
            }
        }
    }

    // Turnoff the executor
    executor.shutdown();
    updateProgress(count.incrementAndGet(), maxCount);

    makeAnimation();
    updateProgress(count.incrementAndGet(), maxCount);

    return System.currentTimeMillis() - startTime;
}
}

我真的不明白为什么Future.isDone()Future.get()这样屏蔽程序!

这是我的第一个问题,所以我希望一切都好。

【问题讨论】:

  • 它是否真的 阻塞在那里(因为线程处于BLOCKED 状态),或者它似乎只是因为忙于等待而停止在那条线上?
  • 顺便说一下,CompletionService 可能更容易在线程完成时收到通知。
  • 另一个顺便说一句:您可以将大部分逻辑拆分为void callInternal(),而不是让return System.currentTimeMillis() - startTime; 分散在您的代码中,而void callInternal() 是从call() { long startTime = ...; callInternal(); return System.currentTimeMillis() - startTime; } 调用的。另外,考虑将executor.shutDown() 放入单个finally 块中。
  • (...或编写一个通用的TimedCallable 类,它包含对委托Callable 的调用(或Runnable,我想),因此您可以在其他地方重用计时逻辑) .

标签: java multithreading javafx-8 java.util.concurrent


【解决方案1】:

我认为如果您使用 CompletionService 实现此操作可能会更容易,它会按照完成的顺序返回您的 Futures。

例如:

Executor executor = Executors.newFixedThreadPool(4);

try {
    CompletionService completionService = new ExecutorCompletionService(executor);

    List<Future<Long>> futures = new ArrayList<>();
    for (Callable<Long> task : task) {
    futures.add(completionService.submit(task));
  }

  int pending = futures.size();
  while (pending > 0) {
      // Wait for up to 100ms to see if anything has completed.
      // The completed future is returned if one is found; otherwise null.
      // (Tune 100ms as desired)
      Future<Long> completed = completionService.poll(100, TimeUnit.MILLISECONDS);
      if (completed != null) {
          updateProgress(count.incrementAndGet(), maxCount);
          --pending;
    }
    if (isCancelled()) {
        // Cancel all task etc.
        break;
    }
  }
} finally {
    executor.shutdown();
}

【讨论】:

    【解决方案2】:

    感谢您给予我的时间和帮助!

    程序似乎很忙,所以我找到了这个解决方案:为了取消所有作业,我使用这个代码:

    if (task != null) {
        task.myCancel();
        task.cancel();
    }
    

    Animation 类变为:

    public class Animation extends Task<Long> {
    [...]
    @Override
    protected Long call() throws Exception {
        long startTime = System.currentTimeMillis();
        ExecutorService executor = null;
    
        try {
            frames = new WritableImage[frameNumber];
            updateProgress(count.incrementAndGet(), maxCount);
            if (isCancelled()) {
                return System.currentTimeMillis() - startTime;
            }
    
            executor = Executors.newWorkStealingPool();
            //executor = Executors.newFixedThreadPool(4);
            updateProgress(count.incrementAndGet(), maxCount);
            if (isCancelled()) {
                return System.currentTimeMillis() - startTime;
            }
    
            tasks = initTasks();
            updateProgress(count.incrementAndGet(), maxCount);
            if (isCancelled()) {
                return System.currentTimeMillis() - startTime;
            }
    
            result = executor.invokeAll(tasks);
            updateProgress(count.incrementAndGet(), maxCount);
    
            makeAnimation();
            updateProgress(count.incrementAndGet(), maxCount);
        }
        catch (InterruptedException ex) {
            System.err.println(ex.toString());
        }
        finally {
            if (executor != null) {
                executor.shutdown();
            }
        }
    
        return System.currentTimeMillis() - startTime;
    }
    [...]
    public void myCancel() {
        tasks.stream().forEach((t) -> {
            ((CartesianFractal)t).myCancel();
        });
    }
    [...]
    }
    

    【讨论】:

      猜你喜欢
      • 2020-01-02
      • 2020-05-10
      • 2017-08-20
      • 2011-08-04
      • 2014-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-14
      相关资源
      最近更新 更多