线程池 | ScheduledThreadPoolExecutor

线程-线程池

认识ScheduledThreadPoolExecutor


前言

concurrent包为我们提供了多种的线程池,其他的几种都是比较常规的,ScheduledThreadPoolExecutor比较特殊,我们就拿它出来学习学习。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210116144647582.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2c2MjU2NjEz,size_16,color_FFFFFF,t_70#pic_center)

提示:以下是本篇文章正文内容

一、ScheduledThreadPoolExecutor是什么?

ScheduledThreadPoolExecutor继承ThreadPoolExecutor对execute和submit进行了重写,

同时也实现了ScheduledExecutorService特有的方法。

其主要的目的就是为了实现周期性执行任务或给定时间延后执行异步任务

二、类的实现与继承

在这里插入图片描述

1.两个重要的内部类

  1. DelayWorkQueue

    主要实现了阻塞队列的接口,可以看出他是一个专门定制的阻塞队列,

  2. ScheduledFutureTask

    ScheduledFutureTask具有FutureTask类的所有功能,并实现了RunnableScheduledFuture接口的所有方法。

    ScheduledFutureTask类的定义如下所示
    在这里插入图片描述

三. 构造方法

//最大线程数是Integer.MAX_VALUE,理论上是一个无限大的线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), handler);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue(), threadFactory, handler);
}

四. 独有方法

//创建并执行一次操作,该操作在给定的延迟后启用。
public ScheduledFuture<?> schedule(Runnable command,
                                   long delay, TimeUnit unit);
//这里传入的是实现Callable接口的任务                           
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay, TimeUnit unit);  
//执行时间将在initialDelay+period后开始执行
//进行检测上一次任务,上次一任务执行完毕,下一个任务才会执行                                                   
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit);  
//initialDelay时间之后开始执行周期性任务                                              
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                 long initialDelay,
                                                 long delay,
                                                 TimeUnit unit);    
                                                

五. 实践

我们要特别注意异常情况下,会打断定时任务。

public static void schedule(ScheduledExecutorService executorService){
    executorService.schedule(() ->{
        for (int i= 0 ; i <10 ;i++) {
            executorService.submit(() -> {
                System.out.println("CurrentThread name:" + Thread.currentThread().getName() + "date:" + Instant.now());

            });
        }
    },5, TimeUnit.SECONDS);
}

public static void scheduleCall(ScheduledExecutorService executorService){
    ScheduledFuture scheduledFuture = executorService.schedule(() -> "scheduleCall",1L,TimeUnit.SECONDS);
    try {
        System.out.println(scheduledFuture.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

public static void scheduleAtFixedRate(ScheduledExecutorService executorService) {
    ScheduledFuture scheduledFuture = executorService.scheduleAtFixedRate(() -> {
                for (int i = 0; i < 10; i++) {
                    executorService.submit(() -> {
                        System.out.println("CurrentThread name:" + Thread.currentThread().getName() + "date:" + Instant.now());
                    });
                }
            }
            , 0, 5L, TimeUnit.SECONDS);
    try {
        System.out.println(scheduledFuture.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

//针对异常情况,会打断定时任务的执行
public static void scheduleWithFixedDelay(ScheduledExecutorService executorService) {
    ScheduledFuture scheduledFuture = executorService.scheduleWithFixedDelay(() -> {
                try {
                    int j = 1 / 0;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            , 10, 5L, TimeUnit.SECONDS);
}

针对于死锁扫描的场景,我们也是可以用到它的

ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
        Runnable dlCheck = () -> {
            long[] threadIds = mbean.findDeadlockedThreads();
            if (threadIds != null) {
                ThreadInfo[] threadInfos = mbean.getThreadInfo(threadIds);
                System.out.println("Detected deadlock threads:");
                for (ThreadInfo threadInfo : threadInfos) {
                    System.out.println(threadInfo.getThreadName()+"11111");
                }
            }
        };
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        // 稍等 5 秒,然后每 10 秒进行一次死锁扫描
        scheduler.scheduleAtFixedRate(dlCheck, 5L, 10L, TimeUnit.SECONDS);
        // 死锁样例代码…

通过每10秒扫描一次,打印出死锁的线程,这是我们可以利用在实际生产中,对于线程的简单监控。

六. 源码环节

参数说明:

参数含义
command要执行的任务
delay延迟时间
unit时间单位

schedule 源码:

public ScheduledFuture<?> schedule(Runnable command,
                                   long delay,
                                   TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    //将提交的任务转换成ScheduledFutureTask,如果period为true则是周期性任务
    RunnableScheduledFuture<?> t = decorateTask(command,
        new ScheduledFutureTask<Void>(command, null,
                                      triggerTime(delay, unit)));
   //任务的主要执行方法                                   
    delayedExecute(t);
    return t;
}

ScheduledThreadPoolExecutor主要就是完成延时或周期性的任务,delayedExecute方法就是主要的实现,
最后通过ensurePrestart方法,添加线程启动任务。

private void delayedExecute(RunnableScheduledFuture<?> task) {
    if (isShutdown())
    //如果线程池已经停止,则调用拒绝策略
        reject(task);
    else {
        //将任务添加入Worker阻塞队列
        super.getQueue().add(task);
        if (isShutdown() &&
            !canRunInCurrentRunState(task.isPeriodic()) &&
            remove(task))
            //取消并删除任务
            task.cancel(false);
        else
        //至少启动一个线程去执行
            ensurePrestart();
    }
}
void ensurePrestart() {
    //通过底29位获取线程池中的线程数量
    int wc = workerCountOf(ctl.get());
    if (wc < corePoolSize)
        addWorker(null, true);
    else if (wc == 0)
        addWorker(null, false);
}

又看到我们熟悉的addWorker方法,我们可以翻阅前面的文章来了解下主要的执行流程。
addWorker的主要流程:

  1. 检查线程池状态
  2. 新建线程,使用Worker进行包装,放入Hashet数组中,最终真正执行任务的线程就放在Worker,所以新增一个addWorker就是新增一个线程。主要实现复用就是Worker类中的runWorker(this)
  3. 启动线程Start()
  4. 添加失败操作,移除Worker,减少WorkerCount

七.总结

主要的三个对象

执行者:Worker

任务:ScheduledFutureTask

执行结果:ScheduledFuture

流程:

  1. 将提交的任务转换成ScheduledFutureTask
  2. 将ScheduledFutureTask添加到Worker队列中
  3. 调用addWorker方法,调用它的核心方法runWorker
  4. 调用getTask方法从阻塞队列中不断的去获取任务进行执行,直到从阻塞队列中获取的任务为 null 的话,线程结束终止
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值