是否存在使用当前线程的ExecutorService?


94

我所追求的是一种兼容的方式来配置是否使用线程池。理想情况下,其余代码完全不会受到影响。我可以使用具有1个线程的线程池,但这不是我想要的。有任何想法吗?

ExecutorService es = threads == 0 ? new CurrentThreadExecutor() : Executors.newThreadPoolExecutor(threads);

// es.execute / es.submit / new ExecutorCompletionService(es) etc

Answers:


69

这是一个非常简单的实现Executor(不要ExecutorService介意),它仅使用当前线程。从“ Java并发实践”(必读)中窃取了该信息。

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}

ExecutorService 是一个更复杂的界面,但可以使用相同的方法进行处理。


4
+1:正如您所说,可以通过子类AbstractExecutorService的相同方式处理ExecutorService。
Paul Cager 2011年

@Paul Yep,AbstractExecutorService看起来像是要走的路。
考虑过2011年

14
在Java8中,您可以将其简化为Runnable::run
Jon Freedman

@Juude它将始终在调用执行程序的线程上运行。
古斯塔夫·卡尔森

同一线程执行器的目的不是能够从execute()内调度更多任务吗?这个答案不会。我找不到满足此要求的答案。
haelix

82

您可以使用番石榴MoreExecutors.newDirectExecutorService()MoreExecutors.directExecutor()也可以不需要ExecutorService

如果包括Guava在内的应用程序过于繁琐,则可以实现几乎相同的功能:

public final class SameThreadExecutorService extends ThreadPoolExecutor {
  private final CountDownLatch signal = new CountDownLatch(1);

  private SameThreadExecutorService() {
    super(1, 1, 0, TimeUnit.DAYS, new SynchronousQueue<Runnable>(),
        new ThreadPoolExecutor.CallerRunsPolicy());
  }

  @Override public void shutdown() {
    super.shutdown();
    signal.countDown();
  }

  public static ExecutorService getInstance() {
    return SingletonHolder.instance;
  }

  private static class SingletonHolder {
    static ExecutorService instance = createInstance();    
  }

  private static ExecutorService createInstance() {
    final SameThreadExecutorService instance
        = new SameThreadExecutorService();

    // The executor has one worker thread. Give it a Runnable that waits
    // until the executor service is shut down.
    // All other submitted tasks will use the RejectedExecutionHandler
    // which runs tasks using the  caller's thread.
    instance.submit(new Runnable() {
        @Override public void run() {
          boolean interrupted = false;
          try {
            while (true) {
              try {
                instance.signal.await();
                break;
              } catch (InterruptedException e) {
                interrupted = true;
              }
            }
          } finally {
            if (interrupted) {
              Thread.currentThread().interrupt();
            }
          }
        }});
    return Executors.unconfigurableScheduledExecutorService(instance);
  }
}

1
对于Android,返回Executors.unconfigurableExecutorService(instance);
Maragues

如果我们使用的只是当前线程,为什么还要同步原语?为什么闩锁?
haelix

@haelix锁存器是必需的,因为即使工作是在与添加工作的线程相同的线程中完成的,但是任何线程都可以关闭执行程序。
NamshubWriter

64

Java 8样式:

Executor e = Runnable::run;


7
绝对肮脏。我喜欢它。
Rogue

这有什么不礼貌?这很优雅:)
lpandzic

这是最好的污秽@Ipandzic,它不寻常且简洁。
Rogue

12

我写了一个ExecutorService基于的AbstractExecutorService

/**
 * Executes all submitted tasks directly in the same thread as the caller.
 */
public class SameThreadExecutorService extends AbstractExecutorService {

    //volatile because can be viewed by other threads
    private volatile boolean terminated;

    @Override
    public void shutdown() {
        terminated = true;
    }

    @Override
    public boolean isShutdown() {
        return terminated;
    }

    @Override
    public boolean isTerminated() {
        return terminated;
    }

    @Override
    public boolean awaitTermination(long theTimeout, TimeUnit theUnit) throws InterruptedException {
        shutdown(); // TODO ok to call shutdown? what if the client never called shutdown???
        return terminated;
    }

    @Override
    public List<Runnable> shutdownNow() {
        return Collections.emptyList();
    }

    @Override
    public void execute(Runnable theCommand) {
        theCommand.run();
    }
}

终止字段不受同步保护。
Daneel S. Yaitskov

1
@ DaneelS.Yaitskov terminated字段将无法从基于此处实际代码的同步访问中受益。在Java中,对32位字段的操作是原子的。
Christopher Schultz

我想上面的isTerminated()方法不太正确,因为isTerminated()仅在当前没有正在执行的任务时才返回true。番石榴跟踪另一个变量中的任务数量,这大概就是为什么它们使用锁保护两个变量的原因。
杰里米·K

6

为了测试目的,我不得不使用相同的“ CurrentThreadExecutorService”,尽管所有建议的解决方案都不错(尤其是提到Guava方式的解决方案),但我想出了与Peter Lawrey 在这里建议的类似的东西。

正如Axelle Ziegler 在此处提到的那样,不幸的是,Peter的解决方案实际上无法正常工作,因为ThreadPoolExecutormaximumPoolSize构造函数参数上引入了检查(即maximumPoolSize,不能是<=0)。

为了避免这种情况,我做了以下工作:

private static ExecutorService currentThreadExecutorService() {
    CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();
    return new ThreadPoolExecutor(0, 1, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), callerRunsPolicy) {
        @Override
        public void execute(Runnable command) {
            callerRunsPolicy.rejectedExecution(command, this);
        }
    };
}

5

您可以使用RejectedExecutionHandler在当前线程中运行任务。

public static final ThreadPoolExecutor CURRENT_THREAD_EXECUTOR = new ThreadPoolExecutor(0, 0, 0, TimeUnit.DAYS, new SynchronousQueue<Runnable>(), new RejectedExecutionHandler() {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        r.run();
    }
});

您只需要其中之一。


聪明!这个问题(诚实的问题)有多安全?在您实际上不想在当前线程中执行任务的地方,有什么方法可以拒绝它?如果ExecutorService关闭或终止,任务是否会被拒绝?
考虑过2011年

由于最大大小为0,因此拒绝所有任务。但是,被拒绝的行为是在当前线程中运行。如果不拒绝任务,只会有问题。
彼得·劳瑞

8
请注意,该政策已经实现,无需定义自己的java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
jtahlborn 2011年

7
再也无法创建最大池大小为0的ThreadPoolExecutor。我想可以使用大小为0的blockingQueue来重现该行为,但是默认的实现似乎不允许这样做。
Axelle Ziegler

如果{。}无法编译,则是java.util.ThreadPoolExecutor中的(corePoolSize <0 || maximumPoolSize <= 0 || maximumPoolSize <corePoolSize || keepAliveTime <0){code}(至少openJdk 7)
Bogdan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.