图解定时任务线程池

线程池概念


咱们上篇文章分析了ThreadPoolExecutor,若是要用一句话说明它的主要优点,就是线程置换。还有Executors工具类,极大的简化了研发人员工做。java

我用一个图重复描述下线程池概念。多生产-多消费模型。面试

image.png


  • 生产者将线程任务丢进线程池中,生产者就就结束了。
  • 线程池控制消费者消费元素,消费者能够是1个或者多个,取决于线程池参数corePoolSize和maxPoolSize设置。
  • 阻塞队列是用来装生产者丢进去的线程任务,如ArrayBlockingQueue,LinkedBlockingQueue,DelayedQueue等。若是生产者生产能力超过消费者消费能力,若是阻塞队列有长度限制而且超过队列长度线程池会执行饱和策略,若是队列没有长度限制,可也能出现OOM哦,由于线程任务可能把内存都撑爆了,这也是面试常考点哦!

详细概念能够翻看我上一篇文章《线程池面试必考问题》。微信



定时任务延时原理


还记得咱们上面说的阻塞队列吗?定时任务线程池底层使用DelayedQueue实现的,这种延迟队列有一个最大的特色:按时出队列,你们都考过驾照吧,科目三考试的时候都是车上坐的是4我的,假设一我的考试须要花15分钟,那么考试学员队列看起来是这样的。多线程

image.png

DelayedQueue底层须要实现Delayed接口同时须要实现getDelay方法和compareTo方法,getDelay方法用于计算出队列时间,一旦小于0就会出队列;compareTo方法用于按触发时间从小到大排序。这就是Schedule线程池任务延时原理,若是须要看案例代码,请参考我文章《并发队列:PriorityBlockingQueue和DelayQueue案例使用》。并发


scheduleWithFixedDelay和scheduleAtFixedRate区别


image.png

由上图可知:假设线程任务:耗时1秒,定时3秒执行,scheduleWithFixedDelay实际上是4秒执行一次。ide

  • scheduleWithFixedDelay:是以任务结束时间周期运行。
  • scheduleAtFixedRate:是以固定周期运行。



FutureTask获取返回值


在ScheduledThreadPoolExecutor中,ScheduledFutureTask是获取定时任务返回值,继承FutureTask。咱们看下FutureTask调用get阻塞简化流程图。工具

image.png

  1. 向线程池添加任务,任务被封装成ScheduledFutureTask而且实现Callable接口是为了获取任务返回值。
  2. 当客户端线程经过get方式获取线程池线程执行的返回值,客户端线程会阻塞直到线程池线程任务执行完。

在实际运用,咱们通常拿返回值测试多线程性能。性能



Timer比较


  1. Timer是单线程,并且不带返回值。ScheduledThreadPoolExecutor是多线程的,采用线程复用代替建立新的线程,而且FutureTask返回值。
  2. Timer线程调用sche方法,若是TimerTask出异常,Timer单线程直接挂掉退出,而ScheduledThreadPoolExecutor会捕获了异常,不影响其余消费者线程。下面是代码测试。
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * @author :jiaolian
 * @date :Created in 2021-02-25 13:50
 * @description:Timer任务异常,Timer线程退出!
 * @modified By:
 * 公众号:叫练
 */
public class TimerTaskExceptionTest {

    public static void main(String[] args) throws InterruptedException {

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("first task");
            }
        },0,1000);

        TimeUnit.SECONDS.sleep(3);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("second task");
                int x = 5/0;
            }
        },0,1000);

    }
}

如上代码:timer提交了first_task和second_task两个任务,3秒后,second_task执行5/0会抛出异常,此时timer线程会退出。若是换成ScheduledThreadPoolExecutor则不会影响first_task。在实际应用中,推荐用定时任务线程池中的方法去处理任务。测试


总结


今天咱们介绍了线程池中面试中几个重要的面试点,整理出来但愿能对你有帮助,写的比不全,同时还有许多须要修正的地方,但愿亲们加以指正和点评,喜欢的请点赞加关注哦。点关注,不迷路,我是叫练【公众号】,微信号【jiaolian123abc】边叫边练。spa

相关文章
相关标签/搜索