Existe um ExecutorService que usa o segmento atual?

O que eu estou depois é uma maneira compatível para configurar o uso de um pool de threads ou não. Idealmente, o resto do código não deve ser afetado de forma alguma. Eu poderia usar um pool de threads com 1 thread, mas isso não é bem o que eu quero. Alguma ideia?

ExecutorService es = threads == 0 ? new CurrentThreadExecutor() : Executors.newThreadPoolExecutor(threads); // es.execute / es.submit / new ExecutorCompletionService(es) etc 

Aqui está uma implementação realmente simples de Executor (e não de ExecutorService , lembre-se de você) que usa apenas o segmento atual. Roubando isso de “Java Concurrency in Practice” (leitura essencial).

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

ExecutorService é uma interface mais elaborada, mas poderia ser tratada com a mesma abordagem.

Você pode usar MoreExecutors.newDirectExecutorService() ou MoreExecutors.directExecutor() se não precisar de um ExecutorService .

Se include Guava é muito pesado, você pode implementar algo quase tão bom:

 public final class SameThreadExecutorService extends ThreadPoolExecutor { private final CountDownLatch signal = new CountDownLatch(1); private SameThreadExecutorService() { super(1, 1, 0, TimeUnit.DAYS, new SynchronousQueue(), 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); } } 

Estilo Java 8:

Executor e = Runnable::run;

Eu escrevi um ExecutorService baseado no 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 shutdownNow() { return Collections.emptyList(); } @Override public void execute(Runnable theCommand) { theCommand.run(); } } 

Você pode usar o RejectedExecutionHandler para executar a tarefa no thread atual.

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

Você só precisa de um desses sempre.

Eu tive que usar o mesmo “CurrentThreadExecutorService” para fins de teste e, embora todas as soluções sugeridas fossem boas (particularmente aquela que menciona o modo Guava ), eu criei algo semelhante ao que Peter Lawrey sugeriu aqui .

Como mencionado por Axelle Ziegler aqui , infelizmente a solução de Peter não funcionará realmente por causa da verificação introduzida no ThreadPoolExecutor no parâmetro construtor maximumPoolSize (ou seja, maximumPoolSize não pode ser < =0 ).

Para contornar isso, fiz o seguinte:

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