定时任务之ScheduledThreadPoolExecutor

java自带的定时任务执行器为ScheduledThreadPoolExecutor,继承于线程池管理器ThreadPoolExecutor,常用方法如下,程序将每2秒输出一次系统时间。

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
executor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println(DateFormatUtils.format(System.currentTimeMillis(), "HH:mm:ss SSS"));
    }
}, 0, 2, TimeUnit.SECONDS);
Thread.sleep(10_000);
executor.shutdown();

java.util.concurrent.ScheduledThreadPoolExecutor#scheduleAtFixedRate方法把参数Runnable对象封装成自定义的定时任务java.util.concurrent.ScheduledThreadPoolExecutor.ScheduledFutureTask

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit) {
    if (command == null || unit == null)
        throw new NullPointerException();
    if (period <= 0)
        throw new IllegalArgumentException();
    // 包装Runnable对象,用于有序阻塞列队DelayedWorkQueue的排序
    ScheduledFutureTask<Void> sft =
        new ScheduledFutureTask<Void>(command,
                                      null,
                                      triggerTime(initialDelay, unit),
                                      unit.toNanos(period));
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    // 把任务放入执行队列,并检查ScheduledThreadPoolExecutor线程数量
    // 如果当前线程池线程数量小于线程池定义的核销线程数量,创建Worker添加新线程
    delayedExecute(t);
    return t;
}

ScheduledThreadPoolExecutor的阻塞队列为其自实现的DelayedWorkQueue,实际上是一个按定时任务RunnableScheduledFuture的下次执行时间排序的有序队列。

ScheduledThreadPoolExecutor中每次添加一个定时任务时,调用方法java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue#offer(java.lang.Runnable)DelayedWorkQueue添加元素。DelayedWorkQueue的具体实现如下,如果队列为空,则元素直接置入为队列首位;如果队列不为空,则队列以任务的下一次执行时间顺序排序。

public boolean offer(Runnable x) {
    if (x == null)
        throw new NullPointerException();
    // DelayedWorkQueue的入参必须为定时任务RunnableScheduledFuture
    RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        int i = size;
        // 队列扩容
        if (i >= queue.length)
            grow();
        size = i + 1;
        if (i == 0) {
            // 空队列,直接为队列首位
            queue[0] = e;
            setIndex(e, 0);
        } else {
            // 按定时任务下次执行时间顺序排序
            siftUp(i, e);
        }
        if (queue[0] == e) {
            leader = null;
            available.signal();
        }
    } finally {
        lock.unlock();
    }
    return true;
}

private void siftUp(int k, RunnableScheduledFuture<?> key) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        RunnableScheduledFuture<?> e = queue[parent];
        if (key.compareTo(e) >= 0)
            break;
        queue[k] = e;
        setIndex(e, k);
        k = parent;
    }
    queue[k] = key;
    setIndex(key, k);
}

ScheduledThreadPoolExecutor添加Worker创建线程调用的父类的方法java.util.concurrent.ThreadPoolExecutor#addWorker,然后新建的线程程调用方法java.util.concurrent.ThreadPoolExecutor#runWorker读取阻塞队列获取待执行的任务。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 核心线程循环阻塞-执行任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 对于非核心线程的线程达到keepAliveTime或允许核销线程超时时,回收超时线程
        processWorkerExit(w, completedAbruptly);
    }
}

这块代码的关键代码为while (task != null || (task = getTask()) != null),作用为读取阻塞队列中的待执行任务。getTask()中Worker线程从阻塞获取元素,而ScheduledThreadPoolExecutorDelayedWorkQueue在获取元素时,如果有任务的下一次执行时间到了则立即返回,否则通过互斥锁阻塞直到有新元素添加或者有元素的下一次执行时间到了。

因此Worker线程在while循环中不断阻塞-执行任务-阻塞,从而实现了循环间隔固定时间执行任务的效果。

ScheduledThreadPoolExecutor是Java并发包中用于定期执行任务的一个工具,它基于ThreadPoolExecutor,添加了调度功能。通过ScheduledThreadPoolExecutor,你可以安排任务在未来某个时间点执行,或者按照一定的时间间隔周期性地运行。 设置定时任务通常包括以下步骤: 1. **创建executor**: 首先,你需要创建一个ScheduledThreadPoolExecutor实例,比如: ```java ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); ``` 这里的数字1表示线程池大小,可以根据需求调整。 2. **提交任务**: 使用`schedule()`方法来指定任务何时开始执行,例如立即执行一次的命令: ```java Runnable task = () -> System.out.println("Task will run once."); executor.schedule(task, 0, TimeUnit.MILLISECONDS); // 0毫秒后执行 ``` 如果你想让任务每隔一段时间执行,可以使用`scheduleAtFixedRate()`或`scheduleWithFixedDelay()`方法,前者会在每个固定时间间隔执行,后者则是第一次延迟执行然后按照给定的间隔重复: ```java executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS); executor.scheduleWithFixedDelay(task, initialDelay, delay, TimeUnit.SECONDS); ``` 这里,initialDelay是首次执行的延迟,period/delay是执行间隔。 3. **取消任务**: 如果需要在任何时候取消任务,可以使用`cancel()`方法配合`Future`接口: ```java Future<?> future = executor.submit(task); future.cancel(true); // 取消任务,true表示立即中断任务 ``` 4. **关闭线程池**: 当所有任务完成后,记得关闭线程池以释放资源: ```java executor.shutdown(); // 或 shutdownNow() 来立即停止所有任务 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值