2

I am making critical use of:

 CompletableFuture
  .delayedExecutor(1, TimeUnit.MILLISECONDS).execute(() -> {});

From what I have read online, it's common for this to use a new thread for every call. I am wondering if there is a way to re-use a thread instead of creating new threads?

Update:

I wasn't clear - I want to use CompletableFuture, but I want CompletableFuture to reuse a certain thread, instead of managing its own threads.

I see this question: CompletableFuture reuse thread from pool

but it recommends using an environment variable - I am wondering if there is a way to do this programmatically.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

2 Answers2

1

Executor new Thread is created for every set of tasks

An Executor is normally used instead of explicitly creating threads. For example, rather than invoking new Thread(new(RunnableTask())).start() for each of a set of tasks, you might use: for each of a set of tasks

Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());

So if you want to reuse the threads, create a thread pool by using ExecutorService or ThreadPoolExecutor, so one of the threads from the pool will execute the runnable tasks.

If all the threads are busy, tasks will be queued up to a certain limit and after that will get rejected through a RejectedExecutionException.

Example

public class NewMain { 

    private static final ExecutorService ex = Executors.newFixedThreadPool(3);

    public static void main(String[] args) {
        Runnable r = () -> System.out.println(Thread.currentThread().getName());
        ex.execute(r);

        CompletableFuture<Void> c = CompletableFuture.runAsync(r, ex);
    }
}

Jdk-8 Use CompletableFuture.runAsync and pass runnable, Executor

public static CompletableFuture runAsync(Supplier supplier, Executor executor)

Returns a new CompletableFuture that is asynchronously completed by a task running in the given executor after it runs the given action.

Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • Hmmm but how does this integrate with CompletableFuture? TMK, CompletableFuture uses its own threads, sometimes many threads. My goal was to tell CompletableFuture to use threads from a specific pool, so I am not sure if this answers my question. – Alexander Mills Feb 07 '19 at 03:43
  • I updated the OP to make the question a little clearer, let me know if it is or not – Alexander Mills Feb 07 '19 at 03:44
1

From what I have read online, it's common for this to use a new thread for every call.

(1) It's the case only if the machine doesn't support parallelism or you made it to not support it by setting the system property java.util.concurrent.ForkJoinPool.common.parallelism to 0 or 1.

8 processors

(2) If the machine does support parallelism, ForkJoinPool.commonPool() is used and the parallelism level is set, I guess, to the number of available processors (which can be determined by Runtime#availableProcessors).

In a scenario with 8 processors, 7-8 threads will probably be created to serve the common ForkJoinPool.

I want to use CompletableFuture, but I want CompletableFuture to reuse a certain thread, instead of managing its own threads.

A DelayedExecutor just submits tasks to the underlying Executor, which is either a ThreadPerTaskExecutor (1) or a ForkJoinPool (2).

Fortunately, you can manually specify an Executor which will be employed by the DelayedExecutor to delegate tasks to.

Executor delayedExecutor = 
     CompletableFuture.delayedExecutor(1, TimeUnit.MILLISECONDS, executor);

It gets us back to your previous question, where I pointed out that an Executor can be defined with a ThreadFactory.

Executor executor = Executors.newCachedThreadPool(YourThreadClass::new);
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142