简体   繁体   English

适用于“主”线程的Java执行器

[英]Java Executor for “main” thread

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: 我想从程序的“主”线程(类似于Android中的主循环程序)创建一个Executor ,然后运行直到它处理了提交给它的所有内容:

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: 但是我得到了一个IllegalThreadState异常:

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. 我可以在新线程上启动ExecutorService ,然后等待它,但这似乎很浪费。

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? 有没有什么好方法可以从当前线程创建Executor ,并等待它处理已提交给它的所有内容?

You are perhaps looking for something like a CompletionService : you submit tasks to this, and it gives you them back in order of completion. 您可能正在寻找类似CompletionService东西:您向此任务提交任务,并且它按完成顺序将其返回。

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 : 如果您能够等到拥有所有东西之后再进行一次处理,则可以使用Guava的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.

Use Guava's MoreExecutors.newDirectExecutorService() 使用番石榴的MoreExecutors.newDirectExecutorService()

This will make sure that the code submitted will be executed in the same thread of the ThreadPool. 这将确保提交的代码将在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);
  }
}

The method invokeAll() documented here waits for all tasks to complete 这里记录的方法invokeAll()等待所有任务完成
You can do this 你可以这样做

executorService.invokeAll();

Follow this post for more explanations 请按照此帖子获取更多说明

You are trying to execute the callback in the main thread. 您正在尝试在主线程中执行回调。 The straightforward approach is to call get() : 直接的方法是调用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 如果要异步执行回调,则可能对CompletionStage感兴趣

CompletableFuture.runAsync(task).thenAccept(r -> /* do something with result */);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM