线程池ThreadPoolExecutor及原理

线程池的创建

     1.Executors直接创建:
        Executors.newCachedThreadPool();        创建可缓存的线程池,(核心线程数为Integer.MAX_VALUE,阻塞队列为同步队列)
        Executors.newFixedThreadPool(n);          创建固定大小线程池(相当于核心线程数与最大线程数相等,阻塞队列为无界队列)
        Executors.newSingleThreadExecutor();   创建单一后台线程(核心线程数和最大线程数都为1,阻塞队列为无界队列)
        Executors.newScheduledThreadPool();      创建执行计划的线程(最大线程数为Integer.MAX_VALUE,阻塞队列为延迟队列)
     2.通过ThreadPoolExecutor创建: 下文介绍。
     3.建议:
           在阿里规范中:线程池不允许使用Executors来创建,而是通过ThreadPoolExecutor来创建, 这样更能明确线
     程池的运行规则,规避资源耗尽的风险,
           Executors的各个弊端:
           1).newFixedThreadPool和newSingleThreadExecutor:
                  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM(内存溢出)。
           2).newCachedThreadPool和newScheduledThreadPool:
                  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

线程池的关闭:

      1.shutdown(): 调用该方法,不会马上终止线程池,而是等所有缓存队列中的任务都执行完才终止,但再也不会接受新的任务

      2.shutdownNow(): 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

ThreadPoolExecutor:

      1.构造方法:
在这里插入图片描述
      2.构造方法参数:

corePoolSizeint核心线程池大小
maximumPoolSizeint最大线程池大小
keepAliveTimelong线程最大空闲时间
unitTimeUnit线程最大空闲时间单位
workQueueBlockingQueue< Runnable >线程阻塞队列
threadFactoryThreadFactory线程创建工厂
handlerRejectedExecutionHandler拒绝策略

      3.一般创建线程池方式:

在这里插入图片描述

      4.参数含义:
            1).corePoolSize: 表示改线程池中核心线程的大小。
                  线程池创建之后,线程池中的线程数为0
                  当任务过来就创建一个核心线程去执行,直到线程数达到corePoolSize 之后,接受的任务放在阻塞队列中。

核心线程:线程池中有两类线程,核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,即使这个核心线程什么都不干(铁饭碗),而非核心线程如果长时间的闲置,就会被销毁(临时工)

            2).maximumPoolSize: 最大线程池大小。
                线程池允许的最大线程数,该值等于核心线程数量 + 非核心线程数量。
                maximumPoolSize肯定是大于等于corePoolSize。
                通过设置corePoolSize和maximumPoolSize相同,可以创建一个FixedThreadPool;
            3).keepAliveTime: 线程最大空闲时间。
                如果线程池当前拥有超过corePoolSize的线程,多余线程(非核心线程)超过keepAliveTime时就会被终止 。
                如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。
                默认情况下,只有线程池中线程数大于corePoolSize 时,keepAliveTime 才会起作用。
            4).unit: keepAliveTime 的单位。
            5).workQueue: 线程阻塞队列。
                当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。
                 workQueue的类型为BlockingQueue< Runnable >,通常可以取下面三种类型:
                a) ArrayBlockingQueue:
                    有界任务队列,基于数组的先进先出队列,此队列创建时必须指定大小;
                b) LinkedBlockingQueue:
                    无界任务队列,基于链表的先进先出队列
                    如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
                c) synchronousQueue:
                    直接提交队列,这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
            6).threadFactory : 线程工厂,用来创建线程。
                Executors的线程池如果不指定线程工厂会使用Executors中的DefaultThreadFactory,
                默认线程池工厂创建的线程都是非守护线程。
            7).handler: 线程池的拒绝策略.
                当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize
                如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
                a). AbortPolicy:
                    线程池默认会采用的是defaultHandler策略,即:
                    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
                    该策略会丢弃任务 并抛出RejectedExecutionException异常。
                b). DiscardPolicy:
                    也是丢弃任务,但是不抛出异常。
                c).DiscardOldestPolicy:
                    线程池会丢弃阻塞队列中末尾的任务 ,然后将被拒绝的任务添加到末尾。
                e).CallerRunsPolicy:
                    当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到”线程池正在运行的线程”中取运行。
                    线程池正在运行的线程:比如在main方法开启线程池,执行拒绝策略时,main的线程就是正在运行的线程

线程池的任务处理策略:

      1.当前线程池中的线程数目小于corePoolSize:
          则每来一个任务,就会创建一个线程去执行这个任务;

      2.当前线程池中的线程数目>=corePoolSize
          则每来一个任务,会尝试将其添加到任务缓存队列当中
          若添加成功,则该任务会等待空闲的核心线程将其取出去执行;
          若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程(非核心线程)去执行这个任务;
          如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

线程池原理:

     1.线程池状态:
          ThreadPoolExecutor类线程池的状态 ,分别为RUNNING、SHURDOWN、STOP、TIDYING、TERMINATED。
          线程池创建后处于RUNNING状态。
          调用shutdown()方法后处于SHUTDOWN状态
          调用shutdownNow()方法后处于STOP状态
          当所有的任务已终止,线程池会变为TIDYING状态。接着会执行terminated()函数。
          线程池处在TIDYING状态时,执行完terminated()方法之后, 线程池被设置为TERMINATED状态。

     2.线程池核心的execute()方法: 处理任务的核心方法是execute
在这里插入图片描述
          ctl.get()是获取线程池状态,用int类型表示。

     为什么要二次检查线程池的状态?
          第二步,入队前进行了一次isRunning判断,入队后,又进行了一次isRunning判断。
          倘若没有二次检查,万一线程池处于非RUNNING状态(在多线程环境下很有可能发生),那么任务永远不会执行

      3.线程复用:
          一个线程在创建的时候会指定一个线程任务,当执行完这个线程任务之后,线程自动销毁。
          但是线程池却可以复用线程,即一个线程执行完线程任务后不销毁,继续执行另外的线程任务。

          在addWorker方法中,会将线程任务firstTask封装到工作线程worker里
          然后再将工作线程add进工作线程组workers里
在这里插入图片描述
在这里插入图片描述
          当t.start();启动线程后,会触发Worker类的run方法调用runWoker()方法。
在这里插入图片描述
          在while循环中,worker会不断地调用getTask()方法从阻塞队列中获取任务。
          只要getTask方法不返回null,此线程就不会退出。
          然后调用task.run()执行任务,从而达到复用线程的目的。

推荐阅读:
     http://concurrent.redspider.group/article/03/12.html

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值