JAVA线程池:ScheduledThreadPoolExecutor

前言

自JDK1.5开始,JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度。在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成,但Timer有不少的缺陷:

  • Timer是单线程模式;
  • 如果在执行任务期间某个TimerTask耗时较久,那么就会影响其它任务的调度;
  • Timer的任务调度是基于绝对时间的,对系统时间敏感;
  • Timer不会捕获执行TimerTask时所抛出的异常,由于Timer是单线程,所以一旦出现异常,则线程就会终止,其他任务也得不到执行。

ScheduledThreadPoolExecutor继承ThreadPoolExecutor来重用线程池的功能,它的实现方式如下:

  • 将任务封装成ScheduledFutureTask对象,ScheduledFutureTask基于相对时间,不受系统时间的改变所影响;
  • ScheduledFutureTask实现了java.lang.Comparable接口和java.util.concurrent.Delayed接口,所以有两个重要的方法:compareTo和getDelay。compareTo方法用于比较任务之间的优先级关系,如果距离下次执行的时间间隔较短,则优先级高;getDelay方法用于返回距离下次任务执行时间的时间间隔;
  • ScheduledThreadPoolExecutor定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间间隔的大小来排序;
  • ScheduledFutureTask继承自FutureTask,可以通过返回Future对象来获取执行的结果。

通过如上的介绍,可以对比一下Timer和ScheduledThreadPoolExecutor:

TimerScheduledThreadPoolExecutor
单线程多线程
单个任务执行时间影响其他任务调度多线程,不会影响
基于绝对时间基于相对时间
一旦执行任务出现异常不会捕获,其他任务得不到执行多线程,单个任务的执行不会影响其他线程

Timer和TimerTask的使用

这个大家应该都是会使用的了,示例代码如下:

Timer  timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.e("time:");

            }
        }, 2000, 40);//2000表示第一次执行任务延迟时间,40表示以后每隔多长时间执行一次run里面的任务

ScheduledThreadPoolExecutor的使用

 SchedualedThreadPoolExecutor 接收SchduledFutureTask类型的任务,是线程池调度任务的最小单位,有三种提交任务的方式:

  •  schedule:延迟多长时间之后只执行一次;
  •  scheduledAtFixedRate:延迟指定时间后执行一次,之后按照固定的时长周期执行;
  •  scheduledWithFixedDelay:延迟指定时间后执行一次,之后按照:上一次任务执行时长 + 周期的时长 的时间去周期执行;

/**
 * 定时任务线程池
 */
public class ScheduledThreadPoolExecutorTest {
    public static void main(String args[]) {
     
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        int time = 2000; // 延迟2秒执行
        MyTask task = new MyTask("定时任务线程");

        // 1、schedule:延迟多长时间之后只执行一次(延迟2秒执行)
        scheduledThreadPool.schedule(task, time, TimeUnit.MILLISECONDS);

        // 2、scheduledAtFixedRate:延迟指定时间后执行一次,之后按照固定的时长周期执行(延迟2秒执行,之后间隔3秒执行)
        //scheduledThreadPool.scheduleAtFixedRate(task,time,3000,TimeUnit.MILLISECONDS);

        // 3、scheduledWithFixedDelay:延迟指定时间后执行一次,之后按照:上一次任务执行时长 + 周期的时长 的时间去周期执行(延迟2秒执行,之后间隔上次任务执行时长+周期时长执行)
        scheduledThreadPool.scheduleWithFixedDelay(task,time,3000,TimeUnit.MILLISECONDS);

        //mScheduledThreadPoolExecutor.shutdown();
    }
}

class MyTask implements Runnable {

    private String TAG = "";

    public MyTask(String tag) {
        TAG = tag;
    }

    @Override
    public void run() {
        System.out.println(TAG + "\t" + System.currentTimeMillis());
    }
}

补充

线程池的创建方法总共有 7 种,但总体来说可分为 2 类:

  • 一类是通过 ThreadPoolExecutor 创建的线程池;
  • 另一个类是通过 Executors 创建的线程池。
  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置,后面会详细讲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值