ジェネリックを使用して「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...>
私のコードに何か他の問題がある場合は、私に知らせてください。
[〜#〜]編集[〜#〜]
削除された実装Callable
上記のコードは抽象化私が本当にやりたいことの:
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;
}
}
EDIT
それを実行するさらに簡単な方法:
@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
を実装し、変数のセッターを持つ独自の特定のオブジェクトを使用できます。
public class CallableWithParam implements Callable<String> {
// protected for subclassing call()
// volatile for multi-threaded reasons
protected volatile int param = 0;
public void setParam(int param) {
this.param = param;
}
@Override
public String call() {
return "my param is: " + param;
}
}
使用法:
@Test
public void process() throws Exception {
CallableWithParam callable = new CallableWithParam() {
@Override
public String call() {
// an anonymous inner class is almost a lambda ;)
return "my param is: " + param + "in subclass";
}
};
callable.setParam(3);
ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>();
List<String> result = executeAlerterTask.process("TaskName", callable);
result.forEach(System.out::println);
}
または、セッターの代わりにコンストラクターでパラメーターを設定することもできます。
まず、call()
はまったく必要ありません。また、Callable<T>
をそのように使用することはないため、このクラスに実装する必要もありません。
呼び出し可能オブジェクトを希望どおりに作成するには、次のようにします。
Callable<String> task; // from the parameter
int i; // from the loop
Callable<String> wrapper = () -> { return task.call() + " on " + i; }
executor.submit(wrapper);
基本的に、ラムダに外部変数i
をパラメーターとして指定します。