114

What I am after is a compatible way to configure the use of a thread pool or not. Ideally the rest of the code should not be impacted at all. I could use a thread pool with 1 thread but that isn't quite what I want. Any ideas?

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

// es.execute / es.submit / new ExecutorCompletionService(es) etc
Michael Rutherfurd
  • 13,815
  • 5
  • 29
  • 40

6 Answers6

96

Java 8 style:

Executor e = Runnable::run;

Lovro Pandžić
  • 5,920
  • 4
  • 43
  • 51
93

You can use Guava's MoreExecutors.newDirectExecutorService(), or MoreExecutors.directExecutor() if you don't need an ExecutorService.

If including Guava is too heavy-weight, you can implement something almost as good:

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);
  }
}
NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
  • 2
    For Android, it's _return Executors.unconfigurableExecutorService(instance);_ – Maragues Nov 28 '18 at 09:45
  • 1
    if all we use is the **current thread**, why synchronization primitives? why the Latch? – haelix Dec 29 '18 at 15:18
  • @haelix the latch is needed because even though the work is done in the same thread as the one that added the work, any thread could shut down the executor. – NamshubWriter Jan 07 '19 at 07:13
76

Here's a really simple Executor (not ExecutorService, mind you) implementation that only uses the current thread. Stealing this from "Java Concurrency in Practice" (essential reading).

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

ExecutorService is a more elaborate interface, but could be handled with the same approach.

overthink
  • 23,985
  • 4
  • 69
  • 69
  • 4
    +1: As you say, an ExecutorService could be handled in the same way, perhaps by subclassing AbstractExecutorService. – Paul Cager Jul 05 '11 at 14:10
  • @Paul Yep, `AbstractExecutorService` looks like the way to go. – overthink Jul 05 '11 at 14:19
  • 21
    In Java8 you can reduce this to just `Runnable::run` – Jon Freedman Apr 15 '15 at 20:10
  • @Juude it will always run on the thread that calls the executor. – Gustav Karlsson Aug 29 '17 at 11:35
  • Isn't the point of a same-thread executor, to be able to schedule more tasks from within execute()? This answer won't do. I can't find an answer that satisfies this. – haelix Dec 29 '18 at 15:16
  • This is exactly how Guava's [`MoreExecutors.directExecutor()`](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/util/concurrent/MoreExecutors.html#directExecutor()) works; the code is conveniently in the API. – Paul Jun 25 '22 at 14:33
20

I wrote an ExecutorService based on the 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();
    }
}
Vladimir Vagaytsev
  • 2,871
  • 9
  • 33
  • 36
Eric Obermühlner
  • 1,076
  • 9
  • 9
  • terminated field is not protected with synchronized. – Daniil Iaitskov Sep 12 '13 at 07:54
  • 1
    @DaneelS.Yaitskov `terminated` field will not benefit from synchronized access based upon the code that is actually here. Operations on 32-bit fields are atomic in Java. – Christopher Schultz Dec 15 '16 at 02:24
  • I suppose the isTerminated() method in the above isn't quite right because isTerminated() is only supposed to return true if there are no currently executing tasks. Guava tracks the number of tasks in another variable, which is presumably why they protect both variables with a lock. – Jeremy K Mar 21 '17 at 23:26
13

I had to use the same "CurrentThreadExecutorService" for testing purposes and, although all suggested solutions were nice (particularly the one mentioning the Guava way), I came up with something similar to what Peter Lawrey suggested here.

As mentioned by Axelle Ziegler here, unfortunately Peter's solution won't actually work because of the check introduced in ThreadPoolExecutor on the maximumPoolSize constructor parameter (i.e. maximumPoolSize can't be <=0).

In order to circumvent that, I did the following:

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);
        }
    };
}
fabriziocucci
  • 782
  • 8
  • 20
6

You can use the RejectedExecutionHandler to run the task in the current thread.

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

You only need one of these ever.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Clever! How safe is this (honest question)? Are there any ways for a task to be rejected where you actually wouldn't want to execute it in the current thread? Are tasks rejected if the ExecutorService is shutting down or terminated? – overthink Jul 05 '11 at 14:24
  • Since the maximum size is 0, every task is rejected. However the rejected behaviour is to run in the current thread. There would only be a problem if the task is NOT rejected. – Peter Lawrey Jul 05 '11 at 14:27
  • 8
    note, there is already an implementation of this policy, no need to define your own `java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy`. – jtahlborn Jul 05 '11 at 14:37
  • 8
    It's not possible anymore to create a ThreadPoolExecutor with a max pool size of 0. I guess it would be possible to reproduce the behaviour using a blockingQueue of size 0, but no default implementation seems to allow that. – Axelle Ziegler Oct 07 '11 at 15:08
  • 1
    that won't compile due to {code} if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0){code} in java.util.ThreadPoolExecutor (at least openJdk 7) – Bogdan Dec 27 '16 at 12:06