一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService(下)

简介: 一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService(下)

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);
    }


这段代码实际运作非常的复杂,但因为大神对其良好的封装,使得我们理解起来并不难。对提交来的新任务处理步骤用一张图描绘如下:


image.png


对于不需要返回值的任务,使用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);
}


针对此接口,非常有必要强调如下几点:


  1. 定时执行和周期性执行是两码事
  2. 通过ScheduledExecutorService#schedule()方法调度执行的任务有且仅会执行一次(当然你任务内部怎么去玩就不归ScheduledExecutorService管喽)
  3. 注意:据我了解这是很多小伙伴的误区,以为ScheduledExecutorService执行的任务都会周期性执行的,这是非常错误的理解哦
  4. 还需要提示一点的是:该接口依旧和线程池木有关系,主要看子类如何去实现。而大神给我们提供为“唯一”实现: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即可。

相关文章
|
6天前
|
消息中间件 前端开发 Java
美团面试:如何实现线程任务编排?
线程任务编排指的是对多个线程任务按照一定的逻辑顺序或条件进行组织和安排,以实现协同工作、顺序执行或并行执行的一种机制。 ## 1.线程任务编排 VS 线程通讯 有同学可能会想:那线程的任务编排是不是问的就是线程间通讯啊? 线程间通讯我知道了,它的实现方式总共有以下几种方式: 1. Object 类下的 wait()、notify() 和 notifyAll() 方法; 2. Condition 类下的 await()、signal() 和 signalAll() 方法; 3. LockSupport 类下的 park() 和 unpark() 方法。 但是,**线程通讯和线程的任务编排是
|
8天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
8天前
|
数据采集 存储 Java
「多线程大杀器」Python并发编程利器:ThreadPoolExecutor,让你一次性轻松开启多个线程,秒杀大量任务!
「多线程大杀器」Python并发编程利器:ThreadPoolExecutor,让你一次性轻松开启多个线程,秒杀大量任务!
|
8天前
|
存储 NoSQL Java
12分钟从Executor自顶向下彻底搞懂线程池
12分钟从Executor自顶向下彻底搞懂线程池
|
8天前
|
存储 算法 Java
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
91 0
|
8天前
|
资源调度 算法 Linux
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
235 0
|
6天前
|
Java 测试技术 Python
Python的多线程允许在同一进程中并发执行任务
【5月更文挑战第17天】Python的多线程允许在同一进程中并发执行任务。示例1展示了创建5个线程打印&quot;Hello World&quot;,每个线程调用同一函数并使用`join()`等待所有线程完成。示例2使用`ThreadPoolExecutor`下载网页,创建线程池处理多个URL,打印出每个网页的大小。Python多线程还可用于线程间通信和同步,如使用Queue和Lock。
25 1
|
8天前
|
存储 安全 Java
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列
|
8天前
|
Dubbo 安全 Java
ThreadPoolExecutor线程池参数及其设置规则
ThreadPoolExecutor线程池参数及其设置规则
19 1
|
8天前
|
Java Spring
定时任务里面的任务多线程操作
该内容是关于Spring Boot中配置异步任务和定时任务的代码示例。首先通过`@Configuration`和`@EnableAsync`开启异步支持,然后定义线程池,如使用`ThreadPoolExecutor`并设置核心线程数、最大线程数等参数。接着,在需要异步执行的方法上添加`@Async`注解。此外,通过`@EnableScheduling`开启定时任务,并使用`@Scheduled`定义具体任务和执行周期。若需指定多个线程池,可以创建不同的`Executor` bean,并在`@Async`中指定线程池名称。
24 2