【多线程】线程池 | ScheduledThreadPoolExecutor

·  阅读 591
【多线程】线程池 | ScheduledThreadPoolExecutor

1. 概要

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

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

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

这个类中还有两个重要的内部类

  1. DelayWorkQueue

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

  1. ScheduledFutureTask

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

    ScheduledFutureTask类的定义如下所示

2. 构造方法

//最大线程数是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);
}
复制代码

3. 独有方法

//创建并执行一次操作,该操作在给定的延迟后启用。
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);    
复制代码

4. 实践

没有实践,理论都是

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);
}
复制代码

5. 源码环节

参数说明: command:要执行的任务 delay:延迟时间 unit:时间单位

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

6.总结

总结

主要的三个对象

执行者:Worker

任务:ScheduledFutureTask

执行结果:ScheduledFuture

流程:

  1. 将提交的任务转换成ScheduledFutureTask
  2. 将ScheduledFutureTask添加到Worker队列中
  3. 调用addWorker方法,调用它的核心方法runWorker
  4. 调用getTask方法从阻塞队列中不断的去获取任务进行执行,直到从阻塞队列中获取的任务为 null 的话,线程结束终止
分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改