Callable lambda expression with argument

lapkritinis picture lapkritinis · Sep 13, 2017 · Viewed 11.6k times · Source

I am trying to implement "TaskExecutor" with generics (my first attempt to use generics) and using ExecutorService.

Here is my "TaskExecutor" class:

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

Here is my way to run it:

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

And here is my question: How to write my Callable that it would accept argument i at a line:

Future<T> future = executor.submit(task);

E.g. desired result would be:

Do something on 1
Do something on 3
Do something on 7
Do something on 2
<...etc...>

If something else is wrong with my code - please let me know.

EDIT

Removed implements Callable

Code above is abstraction of what I really want to do:

  • IntRange really is set of batches where I get data from SQL. Callable really implements logic how to process those SQL batches.

EDIT2

After all suggestions I have following solution:

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

And way to run it:

   @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

Even simpler way to run it:

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

I guess I quite happy with final solution. Thanks all!

Answer

daniu picture daniu · Sep 13, 2017

Firstly, you don't need call() at all, neither do you need to implement Callable<T> in this class because you never use it as such.

To create a callable the way you want, you couldd do

Callable<String> task; // from the parameter
int i; // from the loop
Callable<String> wrapper = () -> { return task.call() + " on " + i; }
executor.submit(wrapper);

You're essentially giving the lambda the outside variable i as a parameter.