在ExecutorService中隐藏一个线程(Java / Clojure)

时间:2011-03-22 21:17:27

标签: java multithreading clojure jvm executorservice

我在clojure程序中创建了相当多的线程:

(import '(java.util.concurrent Executors)) 
(def *pool*   
  (Executors/newCachedThreadPool))

(defn do-something []
  ; work
  Thread/sleep 200
  ; repeat)

(dotimes [i 10000]
  (.submit *pool* do-something)) 

对我来说,JVM之间已经有一段时间了,我基本上在想这里是否有任何反对在Executor正在执行的函数内使用sleep或yield的论据?如果我理解正确,在这种情况下,我的每个工作人员都有自己的线程,因此不会产生任何副作用。

如果执行者正在使用FixedThreadPool:

(Executors/newFixedThreadPool 1000)
事情变得更加复杂,因为线程在工作完成之前不会返回池中,这意味着如果线程处于休眠状态,其他排队的工作人员将需要更长的时间才能完成。

我对此实例中的线程的理解是否正确?

(注意:我怀疑我的设计确实是错的,但只是想确保我在正确的页面上)

2 个答案:

答案 0 :(得分:7)

执行程序在概念上是任务队列+工作池。你对这里发生的事情的解释基本上是正确的。向执行程序提交任务时,工作将排队,直到线程可以执行任务。当它执行任务时,该任务拥有该线程并且休眠将阻止在该工作线程上执行其他任务。

取决于你正在做什么,这可能是好的(尽管在任务中睡觉是不寻常的,可能是不好的形式)。阻止线程作为等待IO的副作用(例如在套接字或数据库调用上被阻塞)更常见。

通常,如果您正在进行定期工作,最好在池外处理它们并在执行它们时触发任务,或者更好的是,使用ScheduledExecutorService代替Executors / newScheduledThreadPool。

Java中用于执行基于时间的任务的另一个主要机制是java.util.Timer,它更容易使用,但不如ScheduledExecutorService强大。

Clojure的另一个替代方法是明确地将worker放入由Clojure管理的后台线程中,而不是由你来管理:

(defn do-task [] 
  (println (java.util.Date.) "doing task"))

(defn worker [f n wait]
            (doseq [task (repeat n f)]
                   (f)
                   (Thread/sleep wait)))

;; use future to execute worker in a background thread managed by Clojure
(future (worker do-task 10 1000))

;; the call to future returns immediately but in the background console
;; you will see the tasks being run.

答案 1 :(得分:-1)

睡眠线程的另一种方法是让每个工作者都有一个“sleepUntil”长值。当执行者调用一个工人时,如果它正在睡觉,它会立即返回。否则,它会完成它的工作,然后返回。这有助于保持线程倒计时,因为如果大多数工作符被标记为正在休眠并快速返回,则FixedThreadPoolExecutor将能够处理比线程更多的工作者。