这篇文章给大家介绍如何进行ScheduledThreadPoolExecutor分析与线程池防坑,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。
专注于为中小企业提供网站设计、成都做网站服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业林州免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
线程池默认使用无界队列,任务过多时,JVM OOM
不设置最大线程数,线程数目暴涨
定时执行,执行一次任务耗时太长甚至一直阻塞,达不到定时执行的目的
线程池内的线程是非守护线程,停止JVM时出问题
使用submit方法,没有调用future.get()导致异常被吞,execute不会有异常被吞的问题
死锁问题,并不常见,但是有时候写复杂了可能会出现。一句话,使用线程池和诸如阻塞队列这种抽象层次比较高的工具时,尽量不要再用低层次的类,如lock,wait(),notify()等,这样混用会导致难以排查的问题。
ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(4); scheduledService.scheduleAtFixedRate(() -> { try { System.out.println("pool thread:" + Thread.currentThread().isDaemon()); System.out.println("start at:" + new Date()); Thread.sleep(10000); System.out.println("end at:" + new Date()); } catch (InterruptedException e) { e.printStackTrace(); } }, 0, 5, TimeUnit.SECONDS);
上面的代码块意图每5秒执行一次任务,但是执行这个任务需要耗费10秒(sleep),那么肯定不能达到5秒一次的效果。分析一下原因。
定时执行功能的实现类是ScheduledThreadPoolExecutor
,它是一个线程池加延迟队列来实现的,延迟队列是使用堆来实现的,也就是根元素值最大或者值最小,在定时任务执行这个场景下,根元素就是下一个要执行的任务,然后有一个等待时间。
public ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); ScheduledFutureTasksft = new ScheduledFutureTask (command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t; }
然后看ScheduledFutureTask
这个类的run()
方法
public void run() { boolean periodic = isPeriodic();//是否周期执行 if (!canRunInCurrentRunState(periodic)) cancel(false); else if (!periodic) ScheduledFutureTask.super.run();//不是执行一次就完了 else if (ScheduledFutureTask.super.runAndReset()) {//执行 setNextRunTime();//设置下次时间 reExecutePeriodic(outerTask);//加入队列 } }
void reExecutePeriodic(RunnableScheduledFuture> task) { if (canRunInCurrentRunState(true)) { super.getQueue().add(task); if (!canRunInCurrentRunState(true) && remove(task)) task.cancel(false); else ensurePrestart();//决定线程池是否新增线程,未达到核心线程数,有新任务则加线程 } }
可以看到如果一次执行时间很长,是达不到定时执行的目的,最终的表现就是一次连着一次执行,如果我们确实需要精准的定期执行,不关注每次多长时间,该怎么办?很简单,再弄一个线程池专门用来干活
关于如何进行ScheduledThreadPoolExecutor分析与线程池防坑就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。