可调用的JavaFX任务

时间:2018-08-13 21:01:35

标签: java multithreading javafx

我正在开发JavaFX应用程序,并以ExecutorService submit方法提供JavaFX任务。另外,我试图在Task对象中提交的返回值中获取Future的返回值。然后,我发现ExecutorService仅在您提交Callable对象时才返回值,尽管具有call方法,JavaFX Tasks还是可运行的。那么有没有解决此问题的方法?

我用这种方式尝试并解决了我的问题,但是当我不想编写自己的类时,我愿意接受建议。

我的主要方法:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Semaphore semaphore = new Semaphore(1);
    List<Integer> list = IntStream.range(0,100).boxed().collect(Collectors.toList());
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()){
        List<Integer> sendingList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            sendingList.add(iterator.next());
        }
        System.out.println("SUBMITTING");
        Future<Integer> future = executorService.submit((Callable<Integer>) new TestCallable(sendingList,semaphore));
        System.out.println(future.get());
        semaphore.acquire();
    }
    executorService.shutdown();
    System.out.println("COMPLETED");
}

我的TestCallable班:

class TestCallable extends Task<Integer> implements Callable<Integer> {

   private Random random = new Random();
   private List<Integer> list;
   private Semaphore semaphore;

   TestCallable(List<Integer> list, Semaphore semaphore) {
       this.list = list;
       this.semaphore = semaphore;
   }

   @Override
   public Integer call(){
       System.out.println("SENDING");
       System.out.println(list);
       try {
           Thread.sleep(1000+random.nextInt(500));
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("RECEIVED");
       semaphore.release();
       return list.size();
   }
}

2 个答案:

答案 0 :(得分:1)

Task扩展了java.util.concurrent.FutureTask,而后者又实现了Future接口。这意味着您可以像使用Task一样使用Future

Executor executor = ...;
Task<?> task = ...;
executor.execute(task);
task.get(); // Future method

这将导致调用get()的线程等待完成。但是,Task的目的是与JavaFX Application Thread交流后台进程的进度。它与GUI的关系密切,这意味着您很可能会从FX线程启动Task。这将导致在您不想要的FX线程上调用get(),因为它将冻结GUI,直到get()返回;您最好直接拨打Task.run

相反,您应该使用Task提供的异步功能。如果要在Task成功完成时检索值,则可以使用onSucceeded属性或收听value / state属性。还有监听失败/取消的方法。

Executor executor = ...;
Task<?> task = ...;
task.setOnSucceeded(event -> handleResult(task.getValue()));
task.setOnFailed(event -> handleException(task.getException()));
executor.execute(task);

如果您不需要Task提供的功能,那么最好直接使用RunnableCallable

答案 1 :(得分:1)

不清楚您要在这里做什么。

首先,您的Semaphore不执行任何操作,因为您使用了Executors.newSingleThreadExecutor(),这已经保证了在任何时间点只能运行一个任务。

第二,就像@Slaw提到的一样,您可能会阻塞JavaFX Application线程,具体取决于您的实际实现(您的示例实际上不是JavaFX应用程序)。

接下来,ExecutorServicesubmit()有2个 main 重载。

第一个重载占用Callable。此重载使您可以检索Callable返回的值(通过对返回的get()调用Future),因为Callable引用的是称为-它可以返回值。

第二个重载取Runnable。由于Task实现了 Future RunnableFuture接口,并且 Future RunnableFuture接口扩展了{{1} }接口,传入Runnable等效于调用此重载。此重载不希望返回结果,因为Task是您运行时没有结果的东西。在此重载返回的Runnable上调用get()将会阻塞,直到任务完成,然后将返回Future。如果您需要检索null返回的值,则需要调用Task中的get(),而不是Task返回的Future

根据OP的评论进行编辑

首先,由于调用方法已经在后台线程中运行,并且预期所有任务将顺序运行(而不是并行运行),因此您应该只运行它们而无需所有这些额外的ExecutorService.submit()和{{1 }},除非有其他原因需要这样做。

第二,ExecutorService对象不过是进行引用的对象。可能真正影响性能的是,您正在将元素的引用复制到新列表中。如果索引已知,则可以使用List.subList(),因为返回的列表将使用与原始列表相同的支持数组,因此无需进行额外的Task复制操作。