Reputation: 6428
I'd like to create an Executor
from the "main" thread of a program (similar to the main looper in Android), and then just run until it has processed everything submitted to it:
public class MyApp {
private static Callable<Integer> task = () -> {
// ... return an int somehow ...
};
public static void main(String[] args) throws ExecutionException, InterruptedException {
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
Thread main = Thread.currentThread();
ExecutorService executorService = Executors.newSingleThreadExecutor(r -> main);
service.submit(task).addListener(() -> {
/// ... do something with the result ...
}, executorService);
executorService.awaitTermination(100, TimeUnit.SECONDS);
}
}
But I get an IllegalThreadState
exception:
SEVERE: RuntimeException while executing runnable MyApp$$Lambda$20/0x00000008000a6440@71f06a3c with executor java.util.concurrent.Executors$FinalizableDelegatedExecutorService@47add263
java.lang.IllegalThreadStateException
at java.base/java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:926)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1343)
at java.base/java.util.concurrent.Executors$DelegatedExecutorService.execute(Executors.java:687)
at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1137)
at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:957)
at com.google.common.util.concurrent.AbstractFuture.set(AbstractFuture.java:726)
at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.afterRanInterruptibly(TrustedListenableFutureTask.java:131)
at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:133)
at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:78)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
I could just start an ExecutorService
on a new thread, and then await that, but that seems wasteful.
Is there a good way to create an Executor
from the current thread, and wait for it to process everything that has been submitted to it?
Upvotes: 1
Views: 5226
Reputation: 35437
MoreExecutors.newDirectExecutorService()
This will make sure that the code submitted will be executed in the same thread of the ThreadPool. I know, it's not the main thread, but at least you don't create other new threads just for the listener, just like you wanted.
import com.google.common.util.concurrent.*;
import org.junit.jupiter.api.Test;
import java.util.concurrent.*;
class ExecutorTest {
private static Callable<Integer> task = () -> {
System.out.println("in call: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(1);
return 0;
};
@Test
void test() throws InterruptedException {
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
ExecutorService executor = MoreExecutors.newDirectExecutorService();
service.submit(task).addListener(() -> {
System.out.println("in listener: " + Thread.currentThread().getName());
}, executor);
executor.awaitTermination(2, TimeUnit.SECONDS);
}
}
Upvotes: 2
Reputation: 140318
You are perhaps looking for something like a CompletionService
: you submit tasks to this, and it gives you them back in order of completion.
ExecutorService service = Executors.newFixedThreadPool(1);
ExecutorCompletionService<Integer> comp = new ExecutorCompletionService<>(service);
comp.submit(task);
// ... I assume you want to submit N tasks.
for (int i = 0; i < N; ++i) {
Future<Integer> future = comp.take();
Integer result = future.get();
// ... do something with the result ...
}
This will process the items as soon as they are complete. If you are able to wait until you have everything, and then process it in one go, you can use Guava's Futures.allAsList
:
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
List<Callable<Integer>> tasks = ...;
List<ListenableFuture<Integer>> futures = new ArrayList<>();
for (Callable<Integer> task : tasks) {
futures.add(service.submit(task));
}
ListenableFuture<List<Integer>> resultsFuture = Futures.allAsList(futures);
List<Integer> results = resultsFuture.get();
// Process the results.
Upvotes: 0
Reputation: 269697
You are trying to execute the callback in the main thread. The straightforward approach is to call get()
:
YourResult result = service.submit(task).get();
/* Do something with your result... */
If you want to execute a callback asynchronously, you might be interested in a CompletionStage
CompletableFuture.runAsync(task).thenAccept(r -> /* do something with result */);
Upvotes: 0