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 ^

[]
Added 3.0

returns all available stacktraces

v 3.0
(defn all-stacktraces
  ([]
   (Thread/getAllStackTraces)))
link
(all-stacktraces) => #(instance? java.util.Map %)

stacktrace ^

[] [thread]
Added 3.0

returns thread stacktrace

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]

thread:active-count ^

[]
Added 3.0

returns active threads

v 3.0
(defn thread:active-count
  ([]
   (Thread/activeCount)))
link
(thread:active-count) => number?

thread:alive? ^

[thread]
Added 3.0

checks if thread is alive

v 3.0
(defn thread:alive?
  ([^Thread thread]
   (.isAlive thread)))
link
(thread:alive? (thread:current)) => true

thread:all ^

[]
Added 3.0

lists all threads

v 3.0
(defn thread:all
  ([]
   (keys (all-stacktraces))))
link
(thread:all) => seq?

thread:all-ids ^

[]
Added 3.0

lists all thread ids

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

thread:current ^

[]
Added 3.0

returns the current thread

v 3.0
(defn thread:current
  ([]
   (Thread/currentThread)))
link
(thread:current) => Thread

thread:daemon? ^

[] [thread]
Added 3.0

checks if thread is a daemon

v 3.0
(defn thread:daemon?
  ([]
   (thread:daemon? (thread:current)))
  ([^Thread thread]
   (.isDaemon thread)))
link
(thread:daemon? (thread:current)) => boolean?

thread:dump ^

[]
Added 3.0

dumps out current thread information

v 3.0
(defn thread:dump
  ([]
   (Thread/dumpStack)))
link
(do (thread:dump) true) => true

thread:global-uncaught ^

[] [f]
Added 3.0

gets and sets the global uncaught exception handler

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

thread:has-access? ^

[thread]
Added 3.0

checks if thread allows access to current

v 3.0
(defn thread:has-access?
  ([^Thread thread]
   (try
     (.checkAccess thread)
     true
     (catch SecurityException e
       false))))
link
(thread:has-access? (thread:current)) => true

thread:has-lock? ^

[lock]
Added 3.0

checks if thread has the lock

v 3.0
(defn thread:has-lock?
  ([^Object lock]
   (Thread/holdsLock lock)))
link
(let [lock (Object.)] (locking lock (thread:has-lock? lock))) => true

thread:id ^

[] [thread]
Added 3.0

returns the id of a thread

v 3.0
(defn thread:id
  ([] (thread:id (thread:current)))
  ([^Thread thread]
   (.getId thread)))
link
(thread:id) => number?

thread:interrupt ^

[thread]
Added 3.0

interrupts a thread

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

thread:interrupted? ^

[thread]
Added 3.0

checks if thread has been interrupted

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

thread:notify ^

[lock]
Added 3.0

notifies threads waiting on lock

v 3.0
(defn thread:notify
  ([^Object lock]
   (locking lock (.notify lock))))
link
(let [lock (Object.)] (thread:notify lock)) => nil

thread:notify-all ^

[lock]
Added 3.0

notifies all threads waiting on lock

v 3.0
(defn thread:notify-all
  ([^Object lock]
   (locking lock (.notifyAll lock))))
link
(let [lock (Object.)] (thread:notify-all lock)) => nil

thread:run ^

[thread]
Added 3.0

runs the thread function locally

v 3.0
(defn thread:run
  ([^Thread thread]
   (.run thread)))
link
(-> (thread {:handler (fn [])}) (thread:run)) => nil

thread:sleep ^

[ms]
Added 3.0

sleeps for n milliseconds

v 3.0
(defn thread:sleep
  ([^long ms]
   (Thread/sleep ms)))
link
(thread:sleep 10) => nil

thread:spin ^

[]
Added 3.0

waits using onSpin

v 3.0
(defn thread:spin
  ([]
   (Thread/onSpinWait)))
link
(thread:spin) => nil

thread:start ^

[thread]
Added 3.0

starts a thread

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]

thread:uncaught ^

[] [thread] [thread f]
Added 3.0

gets and sets the uncaught exception handler

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

thread:wait-on ^

[lock]
Added 3.0

waits for a lock to notify

v 3.0
(defn thread:wait-on
  ([^Object lock]
   (locking lock (.wait lock))))
link
(let [lock (Object.)] (future (thread:sleep 500) (thread:notify lock)) (thread:wait-on lock)) => nil

thread:yield ^

[]
Added 3.0

calls yield on current thread

v 3.0
(defn thread:yield
  ([]
   (Thread/yield)))
link
(thread:yield) => nil