Esiste un ExecutorService che utilizza il thread corrente?

Quello che sto cercando è un modo compatibile per configurare l’uso di un pool di thread o meno. Idealmente il resto del codice non dovrebbe essere influenzato affatto. Potrei usare un pool di thread con 1 thread ma non è proprio quello che voglio. Qualche idea?

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

    Ecco un’implementazione di Executor (not ExecutorService , mind you) davvero semplice che utilizza solo il thread corrente. Rubare questo da “Java Concurrency in Practice” (lettura essenziale).

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

    ExecutorService è un’interfaccia più elaborata, ma potrebbe essere gestita con lo stesso approccio.

    È ansible utilizzare Guava MoreExecutors.newDirectExecutorService() o MoreExecutors.directExecutor() se non è necessario un ExecutorService .

    Se includere Guava è troppo pesante, puoi implementare qualcosa di altrettanto buono:

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

    Stile Java 8:

    Executor e = Runnable::run;

    Ho scritto un ExecutorService basato su 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(); } } 

    È ansible utilizzare RejectedExecutionHandler per eseguire l’attività nel thread corrente.

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

    Hai solo bisogno di uno di questi mai.

    Ho dovuto usare lo stesso “CurrentThreadExecutorService” a scopo di test e, sebbene tutte le soluzioni suggerite fossero belle (in particolare quella che menzionava il modo di Guava ), mi è venuto in mente qualcosa di simile a ciò che Peter Lawrey ha suggerito qui .

    Come menzionato da Axelle Ziegler, sfortunatamente la soluzione di Peter non funzionerà in realtà a causa del controllo introdotto in ThreadPoolExecutor sul parametro constructor maximumPoolSize (ad esempio maximumPoolSize non può essere < =0 ).

    Per aggirare questo, ho fatto quanto segue:

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