在线程池中休眠线程(Java/Clojure)

6

我有一个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是否有任何反对意见?如果我理解正确,在这种情况下,我的每个worker都有自己的线程,因此不应该有任何副作用。

如果Executor正在使用FixedThreadPool:

(Executors/newFixedThreadPool 1000)

由于线程只有在其工作完成后才会返回到池中,因此情况变得更加复杂,这意味着如果线程正在休眠,则其他排队的工作人员完成所需的时间将更长。

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

(注意:我怀疑我的设计实际上是错误的,但只想确保我在正确的轨道上)

2个回答

7
一个执行器概念上是一个任务队列+一个工作线程池。您的解释基本上是正确的。当您向执行器提交任务时,工作将被加入队列,直到有线程可以执行该任务。当它正在执行任务时,该任务拥有该线程,并且睡眠将阻止其他任务在该工作线程上执行。
根据您的操作,这可能没问题(虽然在任务内部睡眠不寻常且可能不好)。通常情况下,阻塞线程是等待I / O的副作用(例如在套接字或db调用上被阻塞)。
一般来说,如果您正在进行周期性工作,则最好在池外处理并在应该执行任务时触发任务,或者更好地使用Executors/newScheduledThreadPool中的ScheduledExecutorService
Java中执行基于时间的任务的另一个主要机制是java.util.Timer,它比ScheduledExecutorService易于使用,但不如后者稳健。
另一个 Clojure 的选择是将工作线程显式地放入由 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
一个替代线程休眠的方法是让每个工作线程拥有一个“sleepUntil”长整型值。当执行器调用一个工作线程时,如果它正在睡眠,它会立即返回。否则,它将执行其工作,然后返回。这可以帮助保持您的线程计数,因为如果大多数工作线程被标记为睡眠并快速返回,FixedThreadPoolExecutor将能够处理比其拥有的线程更多的工作线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接