std.concurrent.thread
thread construction, inspection, and coordination
Use a consistent Clojure interface for common java.lang.Thread operations and configurable thread creation.
1 Overview
The namespace wraps current-thread access, IDs, joining, locks, stack traces, daemon state, uncaught handlers, context classloaders, and thread construction.
2 Walkthrough
2.1 Create and join a thread
(require '[std.concurrent.thread :as thread])
(def worker
(thread/thread
{:name "example-worker"
:daemon true
:handler (fn [] (println :running))
:start true}))
(thread/thread:join worker 1000)
(thread/thread:alive? worker)
2.2 Inspect runtime threads
(thread/thread:current)
(thread/thread:id)
(thread/thread:all-ids)
(thread/thread:active-count)
(thread/stacktrace worker)
2.3 Coordinate through a lock
(def lock (Object.))
(future (thread/thread:wait-on lock))
(thread/thread:notify-all lock)
3 API
- all-stacktraces
- stacktrace
- thread
- thread:active-count
- thread:alive?
- thread:all
- thread:all-ids
- thread:classloader
- thread:current
- thread:daemon?
- thread:dump
- thread:global-uncaught
- thread:has-access?
- thread:has-lock?
- thread:id
- thread:interrupt
- thread:interrupted?
- thread:join
- thread:notify
- thread:notify-all
- thread:run
- thread:sleep
- thread:spin
- thread:start
- thread:uncaught
- thread:wait-on
- thread:yield
v 3.0
(defn all-stacktraces
([]
(Thread/getAllStackTraces)))
link
(all-stacktraces) => #(instance? java.util.Map %)
v 3.0
(defn stacktrace
([]
(stacktrace (thread:current)))
([^Thread thread]
(.getStackTrace thread)))
link
(vec (stacktrace)) => vector?
thread ^
[{:keys [handler daemon priority classloader uncaught name start]}]
Added 3.0
creates a new thread
v 3.0
(defn ^Thread thread
([{:keys [^Runnable handler ^bool daemon priority classloader uncaught name start]}]
(cond-> (Thread. handler)
name (doto (.setName name))
daemon (doto (.setDaemon daemon))
priority (doto (.setPriority priority))
classloader (doto (.setContextClassLoader classloader))
uncaught (doto (.setUncaughtExceptionHandler uncaught))
start (doto (.start)))))
link
(let [thread (thread {:handler (fn []) :name "hello-thread" :daemon true})] [(.getName thread) (thread:daemon? thread)]) => ["hello-thread" true]
v 3.0
(defn thread:alive?
([^Thread thread]
(.isAlive thread)))
link
(thread:alive? (thread:current)) => true
v 3.0
(defn thread:all-ids
([]
(set (map #(.getId ^Thread %) (thread:all)))))
link
(thread:all-ids) => set?
thread:classloader ^
[] [thread] [thread loader]
Added 3.0
gets and sets the context classloader
v 3.0
(defn thread:classloader
([]
(thread:classloader (thread:current)))
([^Thread thread]
(.getContextClassLoader thread))
([^Thread thread loader]
(.setContextClassLoader thread loader)))
link
(let [thread (thread:current) original (thread:classloader thread) loader (.getContextClassLoader (Thread/currentThread))] (try (thread:classloader thread loader) (= loader (thread:classloader thread)) (finally (thread:classloader thread original)))) => true
v 3.0
(defn thread:daemon?
([]
(thread:daemon? (thread:current)))
([^Thread thread]
(.isDaemon thread)))
link
(thread:daemon? (thread:current)) => boolean?
v 3.0
(defn thread:global-uncaught
([]
(Thread/getDefaultUncaughtExceptionHandler))
([f]
(Thread/setDefaultUncaughtExceptionHandler f)))
link
(let [original (thread:global-uncaught) handler (reify Thread$UncaughtExceptionHandler (uncaughtException [_ _ _] nil))] (try (thread:global-uncaught handler) (= handler (thread:global-uncaught)) (finally (thread:global-uncaught original)))) => true
v 3.0
(defn thread:has-access?
([^Thread thread]
(try
(.checkAccess thread)
true
(catch SecurityException e
false))))
link
(thread:has-access? (thread:current)) => true
v 3.0
(defn thread:has-lock?
([^Object lock]
(Thread/holdsLock lock)))
link
(let [lock (Object.)] (locking lock (thread:has-lock? lock))) => true
v 3.0
(defn thread:id
([] (thread:id (thread:current)))
([^Thread thread]
(.getId thread)))
link
(thread:id) => number?
v 3.0
(defn thread:interrupt
([^Thread thread]
(.interrupt thread)))
link
(doto (thread {:handler (fn [] (f/suppress (Thread/sleep 100))) :start true}) (thread:interrupt)) => Thread
v 3.0
(defn thread:interrupted?
([^Thread thread]
(.isInterrupted thread)))
link
(thread:interrupted? (thread:current)) => false
thread:join ^
[] [thread] [thread millis] [thread millis nanos]
Added 3.0
calls join on a thread
v 3.0
(defn thread:join
([]
(thread:join (thread:current)))
([^Thread thread]
(.join thread))
([^Thread thread ^long millis]
(.join thread millis))
([^Thread thread millis nanos]
(.join thread millis nanos)))
link
(thread:join (thread {:handler (fn []) :start true})) => nil
v 3.0
(defn thread:notify
([^Object lock]
(locking lock (.notify lock))))
link
(let [lock (Object.)] (thread:notify lock)) => nil
v 3.0
(defn thread:notify-all
([^Object lock]
(locking lock (.notifyAll lock))))
link
(let [lock (Object.)] (thread:notify-all lock)) => nil
v 3.0
(defn thread:run
([^Thread thread]
(.run thread)))
link
(-> (thread {:handler (fn [])}) (thread:run)) => nil
v 3.0
(defn thread:start
([^Thread thread]
(.start thread)))
link
(let [started (promise) t (thread {:handler (fn [] (deliver started true) (thread:sleep 50))})] (thread:start t) [(deref started 100 false) (do (thread:join t) true)]) => [true true]
v 3.0
(defn thread:uncaught
([]
(thread:uncaught (thread:current)))
([^Thread thread]
(.getUncaughtExceptionHandler thread))
([^Thread thread f]
(.setUncaughtExceptionHandler thread f)))
link
(let [handler (reify Thread$UncaughtExceptionHandler (uncaughtException [_ _ _] nil)) thread (thread {:handler (fn [])})] (thread:uncaught thread handler) (= handler (thread:uncaught thread))) => true