【问题标题】:JavaFX Task CallableJavaFX 任务可调用
【发布时间】:2018-08-13 21:01:35
【问题描述】:

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

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

我的主要方法:

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();
   }
}

【问题讨论】:

  • 您确实注意到 a) Task 实现了 Future b) 提交任务并立即调用 get 导致在调用线程中等待任务完成。你可以直接在线程上执行任务。
  • 如果我有一个多线程执行器,如果没有可用线程,我想等待,而不是每个任务?
  • 打电话给Future.get你一定要等到任务完成。假设提交线程和执行任务的线程具有相同的优先级,在不同的线程上运行任务并不会更快,并且即使使用多线程执行器也不会导致任何任务并行运行。在这种情况下提交任务会将工作从一个线程转移到另一个线程,并使提交任务忙于等待任务完成...

标签: java multithreading javafx


【解决方案1】:

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

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

这将导致调用get() 的线程等待完成。但是,Task 的目的是与 JavaFX 应用程序线程交流后台进程的进度。它与 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

【讨论】:

    【解决方案2】:

    这里不是很清楚你想做什么。

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

    其次,就像@Slaw 提到的那样,您可能会阻塞 JavaFX 应用程序线程,具体取决于您的实际实现(您的示例并不是真正的 JavaFX 应用程序)。

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

    第一个重载采用Callable。此重载允许您检索Callable 返回的值(通过在返回的Future 上调用get()),因为Callable 指的是可以调用的东西 - 它可以返回值。

    第二个重载采用Runnable。由于Task 实现了 Future RunnableFuture 接口,并且 Future RunnableFuture 接口扩展了Runnable 接口,因此传入Task 将是等效的调用这个重载。此重载不希望返回结果,因为 Runnable 是您在没有结果的情况下运行的东西。在此重载返回的Future 上调用get() 将阻塞,直到任务完成,并返回null。如果需要获取Task返回的值,需要调用Taskget(),而不是ExecutorService.submit()返回的Future

    根据 OP 的 cmets 进行编辑

    首先,由于调用方法已经在后台线程中运行,并且所有任务都应该按顺序运行(而不是并行),那么你应该只运行它们而不需要所有这些额外的ExecutorServiceTask,除非必须这样做还有另一个原因。

    其次,List 对象只不过是一个进行引用的对象。真正影响性能的是您将元素的引用复制到新列表中。如果索引已知,您可以使用 List.subList(),因为返回的列表将使用与原始列表相同的后备数组,因此不需要额外的 O(n) 复制操作。

    【讨论】:

    • 我的信号量是为了确保在准备运行新线程之前创建每个子列表。在我的实际应用程序中,列表要大得多,提交它们会占用大量内存并阻塞 CPU。
    • 另外,在我的应用程序中,我在另一个后台守护线程上运行执行程序,因此它不会冻结我的 GUI。这只是一个测试代码。
    • "由于Task 实现了Future 接口,而Future 接口扩展了Runnable 接口..."。 Future 不扩展 Runnable。我相信您正在考虑RunnableFutureTask 确实通过扩展FutureTask 来实现)。
    猜你喜欢
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    相关资源
    最近更新 更多