栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

《Java并发编程的艺术》读后笔记-ScheduledThreadPoolExecutor详解(第十章)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《Java并发编程的艺术》读后笔记-ScheduledThreadPoolExecutor详解(第十章)

文章目录

《Java并发编程的艺术》读后笔记-ScheduledThreadPoolExecutor详解(第十章)

1.ScheduledThreadPoolExecutor简介2.ScheduledThreadPoolExecutor的运行机制3.ScheduledThreadPoolExecutor的实现

《Java并发编程的艺术》读后笔记-ScheduledThreadPoolExecutor详解(第十章) 1.ScheduledThreadPoolExecutor简介

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。

ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数

2.ScheduledThreadPoolExecutor的运行机制

ScheduledThreadPoolExecutor的执行示意图:

DelayQueue是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没有什么意义(设置maximumPoolSize的大小没有什么效果)

ScheduledThreadPoolExecutor的执行主要分为两大部分:

    当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask。线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改:

    使用DelayQueue作为任务队列。获取任务的方式不同执行周期任务后,增加了额外的处理
3.ScheduledThreadPoolExecutor的实现

前面我们提到过,ScheduledThreadPoolExecutor会把待调度的任务ScheduledFutureTask放到一个DelayQueue中。

ScheduledFutureTask主要包含3个成员变量:

    long型成员变量time,表示这个任务将要被执行的具体时间。long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。long型成员变量period,表示任务执行的间隔周期。

DelayQueue封装了一个PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序:

排序时,time小的排在前面(时间早的任务将被先执行)。如果两个ScheduledFutureTask的time相同,就比较sequenceNumber,sequenceNumber小的排在前面(也就是说,如果两个任务的执行时间相同,那么先提交的任务将被先执行)。

ScheduledThreadPoolExecutor中的线程执行周期任务的过程:

这四个步骤具体说明:

    线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前时间。线程1执行这个ScheduledFutureTask。线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。

DelayQueue.take()方法的源代码实现:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        for (;;) {
            E first = q.peek();
            if (first == null)
                available.await();
            else {
                long delay = first.getDelay(NANOSECONDS);
                if (delay <= 0)
                    return q.poll();
                first = null; // don't retain ref while waiting
                if (leader != null)
                    available.await();
                else {
                    Thread thisThread = Thread.currentThread();
                    leader = thisThread;
                    try {
                        available.awaitNanos(delay);
                    } finally {
                        if (leader == thisThread)
                            leader = null;
                    }
                }
            }
        }
    } finally {
        if (leader == null && q.peek() != null)
            available.signal();
        lock.unlock();
    }
}

DelayQueue.take()的执行示意图:

如图所示,获取任务分为3大步骤:

    获取Lock获取周期任务

    如果PriorityQueue为空,当前线程到Condition中等待如果PriorityQueue的头元素的time时间比当前时间大,到Condition中等待到time时间获取PriorityQueue的头元素;如果PriorityQueue不为空,则唤醒在Condition中等待的所有线程 释放Lock

ScheduledThreadPoolExecutor在一个循环中执行步骤2,直到线程从PriorityQueue获取到一个元素之后(执行2.3.1之后),才会退出无限循环(结束步骤2)。

DelayQueue.add()的源代码实现:

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        q.offer(e);
        if (q.peek() == e) {
            leader = null;
            available.signal();
        }
        return true;
    } finally {
        lock.unlock();
    }
}

DelayQueue.add()的执行示意图:

如图所示,添加任务分为3大步骤

    获取Lock添加任务:

    向PriorityQueue添加任务。如果在上面2.1中添加的任务是PriorityQueue的头元素,唤醒在Condition中等待的所有线程。 释放Lock

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/726315.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号