未来是否总是创建一个新线程?

Does future always create a new thread?

假设我这样做:

(future
  (do-the-thing))

我能保证,无论 (do-the-thing) 做什么,

如果不是,在什么情况下这些假设是错误的?

简短的回答是

来自 clojure 的 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)]
...

所以未来的执行人是clojure.lang.Agent/soloExecutor.

来自 Agent.java:

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

可以看到soloExecutor是由Executors.newCachedThreadPool()

创建的

来自 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.

所以答案是 (do-the-thing) 的一些其他作业可能在同一个线程中执行,如果没有更多作业,线程将在 60 秒后终止。

您可以在以下代码中确认 Executors.newCachedThreadPool 的行为:

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

在 clojure 控制台中执行此代码,您将获得:

50 49 48 47 46 45 44 43 42 41 nil

第一次。 5秒后再次执行,得到:

50 49 43 41 45 42 46 47 48 44 nil

因此可以确认线程被重用了。

如果您在 60 秒后执行相同的代码,您将得到:

60 59 58 57 56 55 54 53 52 51 nil

因此您可以确认之前的线程已终止并创建了新线程。