newScheduledThreadPool(1) 和 newSingleThreadScheduledExecutor() 的区别

Posted

技术标签:

【中文标题】newScheduledThreadPool(1) 和 newSingleThreadScheduledExecutor() 的区别【英文标题】:Difference between newScheduledThreadPool(1) and newSingleThreadScheduledExecutor() 【发布时间】:2015-07-13 18:16:55 【问题描述】:

我想知道 Executors 类的这两种方法有什么区别?我有一个 Web 应用程序,我每 100 毫秒检查一次数据,这就是为什么我将这个调度程序与 scheduleWithFixedDelay 方法一起使用。我想知道在这种情况下我应该使用哪种方法(newScheduledThreadPool 或 newSingleThreadScheduledExecutor)? 我还有一个问题——在我监控 Glassfish 服务器的 VisualVM 中,我注意到我有一些线程处于 PARK 状态——例如:

java.lang.Thread.State: WAITING
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for <3cb9965d> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

这些线程是否可能与调度程序连接,因为我不知道还有什么会创建它们?这些线程永远不会被破坏,所以我担心这会引起一些麻烦。这是一个屏幕截图(新的 Thread-35 将在 15 分钟内创建,依此类推...):

【问题讨论】:

线程池是一个线程池/线程组,具有预先配置的线程数(开始时)。如果您有一个工作密集型场景,其中池中的每个线程可能工作更长的时间并且您希望更快地完成工作,因此您创建一个线程池,这很有用。单线程——顾名思义,就是一个线程一遍又一遍地执行。根据工作性质和任务的复杂性,您可以在单个线程或线程池之间进行选择。 【参考方案1】:

正如documentation 所说:

与其他等效的 newScheduledThreadPool(1) 不同,返回的执行程序保证不可重新配置以使用其他线程。

所以当使用newScheduledThreadPool(1)你以后可以添加更多的线程。

【讨论】:

是的,我以前读过这个。那么以后添加更多线程有区别吗?这个语句怎么样(来自newSingleThreadExecutor的文档):“(但是请注意,如果这个单线程在关闭之前的执行过程中由于失败而终止,如果需要执行后续任务,一个新线程将取代它。)” - 也这样做是否申请newScheduledThreadPool(1)? 是的,它也适用。 实际区别在于newScheduledThreadPool(N)N > 1,将保持多个线程处于空闲状态。计划任务可以在不同的线程中运行,但它们遵循happen-before 模型,在这种情况下,这基本上意味着,没有两个计划任务将永远并行运行(计划在完全相同的执行时间的任务在先进先入中启用-先出(FIFO)提交顺序)。如果当前线程死亡,可以简单地从池中取出一个新线程,这可能会更快。 Executor 会保持线程数为N【参考方案2】:

newSingleThreadScheduledExecuto() 由委托包装,如您在 Executors.java 中所见:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() 
    return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));

差异(来自 javadoc):

如果这个单线程在关闭前的执行过程中由于失败而终止,如果需要执行后续任务,一个新线程将取代它。

与其他等效的 @code newScheduledThreadPool(1) 不同,返回的执行程序保证不能重新配置以使用其他线程。

回复您的评论:

这是否也适用于 newScheduledThreadPool(1)?

不,您需要自己处理线程故障。

关于 Unsafe.park(),见this。

【讨论】:

好的,谢谢。所以就我而言,我认为使用 newSingleThreadScheduledExecutor() 很好。由于使用 newSingleThreadScheduledExecutor(),我是否有可能拥有这些 PARK 状态线程(参见上面的屏幕截图)?编辑:我已经看到有关 Unsafe.park() 的话题,但我无法弄清楚是什么原因造成的,所以我想知道是否有人知道调度程序是否存在任何问题...... @user4341206 抱歉,我不确定。但似乎很有可能。因为单线程即使在空闲时也会保留在线程池中。 当旧线程未及时完成时,仍会向池中添加新任务。 上述问题的答案“这是否也适用于 newScheduledThreadPool(1)?”是是的。传递给ScheduledThreadPoolExecutor 的构造函数的1corePoolSize。除非您将其设置为 0(不建议这样做),否则如果先前执行的线程因异常而死亡并且需要一个,ScheduledThreadPoolExecutor 将重新创建线程。 newSingleThreadScheduledExecutor()newScheduledThreadPool(1) 之间的唯一区别在于,在第一种情况下,保证执行器不能被重新配置以使用额外的线程。 不同之处在于 DelegatedScheduledExecutorService 只公开了scheduleinvoke 相关的方法。不能从中调用所有其他配置方法。【参考方案3】:

正如wings和tagir所指出的,区别在于“如何管理失败”。

关于线程你的线程处于等待状态; park 不是一种状态,而是一种将线程置于等待状态的方法;另见

How to detect thread being blocked by IO?

但是,让我提出一种在 Java EE 上实现调度线程的不同方法;你应该看看 EJB 的TimerService

@Singleton
public class TimerSessionBean 
    @Resource
    TimerService timerService;   

    public void setTimer(long intervalDuration) 
        Timer timer = timerService.createTimer(intervalDuration, 
                "Created new programmatic timer");
    

    @Timeout
    public void lookForData(Timer timer) 
        //this.setLastProgrammaticTimeout(new Date());
        ....
    

    //OR
    @Schedule(minute = "*/1", hour = "*")
    public void runEveryMinute() 
      ...
    

   

【讨论】:

谢谢,我也去看看TimerService EJB。

以上是关于newScheduledThreadPool(1) 和 newSingleThreadScheduledExecutor() 的区别的主要内容,如果未能解决你的问题,请参考以下文章

线程池Executors.newScheduledThreadPool-只执行1次

使用newScheduledThreadPool来模拟心跳机制

java线程池之newScheduledThreadPool

运用Executors.newScheduledThreadPool的任务调度怎么解决

Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

线程池newScheduledThreadPool使用