【发布时间】:2018-02-22 02:44:15
【问题描述】:
我正在尝试使用泛型实现“TaskExecutor”(我第一次尝试使用泛型)并使用 ExecutorService。
这是我的“TaskExecutor”类:
public class ExecuteAlerterTask<T> {
public List<T> process(String executorName, Callable<T> task) throws ExecutionException, InterruptedException {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(executorName + "-%d")
.setDaemon(true)
.build();
ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory);
Collection<Future<T>> futures = new ArrayList<>();
IntStream.range(1, 10).forEach(i -> {
Future<T> future = executor.submit(task);
futures.add(future);
});
List<T> result = new ArrayList<>();
for (Future<T> f : futures) {
result.add(f.get());
}
executor.shutdown();
return result;
}
}
这是我的运行方式:
@Test
public void process() throws Exception {
Callable<String> callable = () -> "Do something on ";
ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>();
List<String> result = executeAlerterTask.process("TaskName", callable);
result.forEach(System.out::println);
}
这是我的问题: 如何编写我的 Callable 以使其在一行中接受参数 i:
Future<T> future = executor.submit(task);
例如期望的结果是:
Do something on 1
Do something on 3
Do something on 7
Do something on 2
<...etc...>
如果我的代码有其他问题 - 请告诉我。
编辑
移除了可调用的实现
上面的代码是我真正想做的抽象:
- IntRange 确实是一组我从 SQL 获取数据的批次。可调用 真正实现了如何处理这些 SQL 批处理的逻辑。
EDIT2
毕竟我有以下解决方案:
public class ExecuteAlerterTask<T> {
public List<T> process(String executorName, Collection<Callable<T>> task) throws ExecutionException, InterruptedException {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(executorName + "-%d")
.setDaemon(true)
.build();
ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory);
Collection<Future<T>> futures = executor.invokeAll(task);
List<T> result = new ArrayList<>();
for (Future<T> f : futures) {
result.add(f.get());
}
executor.shutdown();
return result;
}
}
以及运行方式:
@Test
public void process() throws Exception {
Collection<Callable<String>> tasks = new ArrayList<>();
IntStream.range(1, 10).forEach(i -> {
tasks.add(new Task(i).callable);
});
ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>();
List<String> result = executeAlerterTask.process("TaskName", tasks);
result.forEach(System.out::println);
}
private class Task {
private int i;
private Callable<String> callable = () -> "Doing something on i: " + i;
private Task(int i) {
this.i = i;
}
}
EDIT3
更简单的运行方式:
@Test
public void process() throws Exception {
Collection<Callable<String>> tasks = new ArrayList<>();
IntStream.range(1, 10).forEach(i -> {
tasks.add(() -> "Do something on i: " + i * 2);
});
ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>();
List<String> result = executeAlerterTask.process("TaskName", tasks);
result.forEach(System.out::println);
}
我想我对最终的解决方案很满意。谢谢大家!
【问题讨论】:
-
不使用
Callable为什么还要实现? -
@RealSkeptic 好的,知道了。这回答了如何摆脱 T call()
-
有了新的编辑,它看起来更像是你做错了。如果你想干净地提供一个 Executor,你提交的
Callable<>应该有它需要的所有信息。批次的迭代需要在外部完成。或者你把方法改成process(Collection<Callable<>>)。 -
Callable本质上是一个Supplier。您可以使用Function提供参数并返回结果,但必须由执行者提供参数。然后你必须提供一个参数供应商……是的,它可以完成,但它不会很漂亮。 -
@lapkritinis 是的,这看起来很简单——如果你甚至需要一个类,只需 lambda 就足以产生输出。取决于
i的来源。