execute()方法执行分析
ThreadPoolExecutor: public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
这段代码实际运作非常的复杂,但因为大神对其良好的封装,使得我们理解起来并不难。对提交来的新任务处理步骤用一张图描绘如下:
对于不需要返回值的任务,使用submit or execute效果一样,但一般情况下推荐统一使用更高层的submit系列方法去提交你的任务。
代码示例
略(相信没有人不会用它吧)。
ScheduledExecutorService
它在ExecutorService接口上再扩展,额外增加了定时、周期执行的能力。
public interface ScheduledExecutorService extends ExecutorService { // ========这个两个方法提交的任务只会执行一次======== // 创建并执行启用的一次性操作在给定的延迟之后。 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); // 创建并执行启用的一次性操作在给定的延迟之后。 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); // ========这个两个方法提交的任务会周期性的执行多次======== // 在给定的初始延迟之后,以给定的时间间隔执行周期性动作。 // 即在 initialDelay 初始延迟后initialDelay + period 执行第一次 // initialDelay + 2 * period 执行第二次,依次类推 // 特点:下一次任务的执行并不管你上次任务是否执行完毕 // 所以它名叫FixedRate:固定的频率执行(每次都是独立事件) public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); // 给定的初始延迟之后首先启用的定期动作 // 随后**上一个执行的终止**和**下一个执行的开始之间**给定的延迟。 // 也就是说delay表示的是上一次的end和下一次start之间的时间,两次之间是有关系的,不同于上个方法 // 所以它名叫FixedDelay:两次执行间固定的延迟 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); }
针对此接口,非常有必要强调如下几点:
- 定时执行和周期性执行是两码事
- 通过ScheduledExecutorService#schedule()方法调度执行的任务有且仅会执行一次(当然你任务内部怎么去玩就不归ScheduledExecutorService管喽)
- 注意:据我了解这是很多小伙伴的误区,以为ScheduledExecutorService执行的任务都会周期性执行的,这是非常错误的理解哦
- 还需要提示一点的是:该接口依旧和线程池木有关系,主要看子类如何去实现。而大神给我们提供为“唯一”实现:ScheduledThreadPoolExecutor。
ScheduledThreadPoolExecutor 集大成者
它可谓线程池 + 执行器的集大成者,最强子类:在线程池里执行任务,并且还可以定时、周期性的执行。
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService { ... }
代码示例
略。
关于它有话说:如果你有定时、周期执行任务的需求,请使用ScheduledThreadPoolExecutor来代替Java古老的Timer/TimerTask API吧。
关于它俩的对比,可参考这篇文章:Java定时任务ScheduledThreadPoolExecutor详解以及与Timer、TimerTask的区别(执行指定次数停止任务)
关于Executors
通过命名可知它是用于创建Executor执行器的工具类(均为静态方法):
- 可快捷创建带有线程池能力的执行器ThreadPoolExecutor(ExecutorService)
- 可快速创建具有线程池,且定时、周期执行任务能力的ScheduledThreadPoolExecutor(ScheduledExecutorService)
- …
另外,它还提供几个我认为比较好用的工具方法分享给大家:
Executors: // 默认线程工厂: // 线程名称为:pool-[poolNum]-thread-[threadNumber] // 非守护线程(请注意:非守护线程,看看是否符合你的要求) public static ThreadFactory defaultThreadFactory() { return new DefaultThreadFactory(); } // 很方便的把一个Runnable适配为一个Callable<T> // result可以是个常量值。当然也可以是null public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } public static Callable<Object> callable(Runnable task) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<Object>(task, null); }
关于Executors最后我想说:在生产环境下禁用,禁用,禁用(原因请参照阿里编码规范),一般用于自己测试的时候快速构建方便而为之。
使用线程池,请务必对其七大参数烂熟于胸,否则不要使用,容易酿成大祸或,并且还是软病,很难排查,因此需要敬畏和谨慎。
总结
关于Java中的Executor执行器大体系,以及它和线程池是什么关心就介绍到这,我相信经过本文你应该能彻底了解该体系的框架了吧,不用每次都不知道使用哪个了。
ScheduledExecutorService属于最强接口,它具有全部能力,不过一般若你并不需要定时/周期执行能力的时候,请使用ThreadPoolExecutor/ExecutorService即可。