Sam Estep
Sam Estep

Reputation: 13324

Does future always create a new thread?

Let's say I do this:

(future
  (do-the-thing))

Am I guaranteed that, regardless of what (do-the-thing) does,

If not, under what circumstances would these assumptions be false?

Upvotes: 9

Views: 2736

Answers (1)

ymonad
ymonad

Reputation: 12090

Short answer is No

From clojure's core.clj:

(defmacro future
...
  [& body] `(future-call (^{:once true} fn* [] ~@body)))
...

(defn future-call 
...
  [f]
  (let [f (binding-conveyor-fn f)
        fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]
...

So the executor of the future is clojure.lang.Agent/soloExecutor.

From Agent.java:

volatile public static ExecutorService soloExecutor = Executors.newCachedThreadPool(
    createThreadFactory("clojure-agent-send-off-pool-%d", sendOffThreadPoolCounter));

You can see that soloExecutor is created by Executors.newCachedThreadPool()

From document of Executors.newCachedThreadPool:

Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.

So the answer is some other job of (do-the-thing) may be executed in same thread, and the thread will be terminated after 60 seconds if there's no more job.

You can confirm the behavior of Executors.newCachedThreadPool in following code:

(doseq [i (for [x (range 10)] (future (Thread/sleep 1000) (.getId (Thread/currentThread))))] (print @i) (print " "))

Executing this code in clojure console, you get:

50 49 48 47 46 45 44 43 42 41 nil

for first time. And execute it again after 5 seconds, you get:

50 49 43 41 45 42 46 47 48 44 nil

Therefore you can confirm that the thread is reused.

If you execute same code after 60 seconds, you get:

60 59 58 57 56 55 54 53 52 51 nil

So you can confirm that previous threads were terminated and new threads were created.

Upvotes: 9

Related Questions