我正在开发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();
}
}
答案 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
提供的功能,那么最好直接使用Runnable
或Callable
。
答案 1 :(得分:1)
不清楚您要在这里做什么。
首先,您的Semaphore
不执行任何操作,因为您使用了Executors.newSingleThreadExecutor()
,这已经保证了在任何时间点只能运行一个任务。
第二,就像@Slaw提到的一样,您可能会阻塞JavaFX Application线程,具体取决于您的实际实现(您的示例实际上不是JavaFX应用程序)。
接下来,ExecutorService
对submit()
有2个 main 重载。
第一个重载占用Callable
。此重载使您可以检索Callable
返回的值(通过对返回的get()
调用Future
),因为Callable
引用的是称为-它可以返回值。
第二个重载取Runnable
。由于Task
实现了 Future
RunnableFuture
接口,并且 Future
RunnableFuture
接口扩展了{{1} }接口,传入Runnable
等效于调用此重载。此重载不希望返回结果,因为Task
是您运行时没有结果的东西。在此重载返回的Runnable
上调用get()
将会阻塞,直到任务完成,然后将返回Future
。如果您需要检索null
返回的值,则需要调用Task
中的get()
,而不是Task
返回的Future
。>
首先,由于调用方法已经在后台线程中运行,并且预期所有任务将顺序运行(而不是并行运行),因此您应该只运行它们而无需所有这些额外的ExecutorService.submit()
和{{1 }},除非有其他原因需要这样做。
第二,ExecutorService
对象不过是进行引用的对象。可能真正影响性能的是,您正在将元素的引用复制到新列表中。如果索引已知,则可以使用List.subList(),因为返回的列表将使用与原始列表相同的支持数组,因此无需进行额外的Task
复制操作。