线程池

作者: sizuoyi00 | 来源:发表于2019-12-03 00:40 被阅读0次

    使用场景

    执行时间短,需要处理的任务量比较大

    优势
    1.重用线程,创建(用户空间->内核空间->内核线程)的销毁线程的开销,提高性能
    2.提高响应速度,因为不需要创建
    3.统一管理,对线程进行一些维护和管理,比如定时开始,周期执行,并发数控制等等

    ThreadPoolExecutor

    构造方法
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
        }
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
        }
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
        }
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
            if (corePoolSize < 0 ||  maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    
    核心参数说明
    • corePoolSize
      核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制,除非将allowCoreThreadTimeOut设置为true。
      提交任务时,当运行的线程数小于corePoolSize时,将创建一个新线程来处理请求,即使其他工作线程是空闲的。
    • maximumPoolSize
      线程池所能容纳的最大线程数,如果有多于corePoolSize但小于maximumPoolSize的线程在运行,则只有在队列满时才会创建新线程。通过将corePoolSize和maximumPoolSize设置为相同的值,可以创建一个固定大小的线程池。
    • keepAliveTime
      非核心线程的闲置超时时间,超过这个时间就会被回收。核心线程不受此控制,除非将allowCoreThreadTimeOut设置为true。
    • unit
      指定keepAliveTime的单位,如TimeUnit.SECONDS。
    • workQueue
      线程池中的任务队列,核心线程数满了后添加到任务队列。
      常用的有三种队列:参考阻塞队列文章
      LinkedBlockingDeque:指定大小代表定长队列,不指定相当于无界队列。
      ArrayBlockingQueue:定长队列。
      SynchronousQueue:无缓存队列,不存储元素。
    • threadFactory
      线程创建工厂,提供创建新线程的功能,接口中只有newThread(Runnable r)方法
    • handler
      拒绝策略,当线程池资源不足(核心线程数,任务队列,最大线程数都占满),采用拒绝策略。常见拒绝策略:
      ThreadPoolExecutor.AbortPolicy:默认拒绝策略,丢弃任务并抛错处理
      ThreadPoolExecutor.CallerRunsPolicy:由调用者线程执行该任务,如果执行器已经关闭则丢弃任务。
      ThreadPoolExecutor.DiscardPolicy:丢弃任务,不执行任何操作。
      ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任,重新尝试执行任务,如果执行器已经关闭则丢弃任务。

    Executors创建线程池(禁用)

    • newSingleThreadExecutor
      单线程线程池,无界队列,出现异常则重新创建一个代替。
      new ThreadPoolExecutor(1, 1, 0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
    • newFixedThreadPool
      固定大小线程池,无界队列,出现异常则会补充一个新线程。
      new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
    • newCachedThreadPool
      可缓存线程池,SynchronousQueue无缓冲队列,如果没有可用的现有线程,将创建一个新的线程并将其添加到池中。已经60秒没有使用的线程将被终止并从缓存中删除。因此,长时间保持空闲的池不会消耗任何资源。
      new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())
    阿里巴巴编程规范对于线程池的要求

    线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    说明:Executors 返回的线程池对象的弊端如下:
    1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

    线程池状态分析

    • running:能够接收新任务,并且对已添加任务进行处理。
      线程池初始化状态,线程一创建状态为running,初始线程数任务数为0
    • shutdown:不接收新任务,可以对已添加任务进行处理。
      调用线程池shutdown()方法running->shutdown
    • stop:不可以添加新任务,不处理已添加任务,正在处理的任务中止操作。
      调用线程池shutdownNow()方法,running/shutdown->stop
    • tidying:所有任务已终止,ctl记录的任务数量为0,当线程池状态为tidying时,会调用钩子函数terminated()。
      shutdown状态阻塞队列为空,并且线程中执行的任务为空,转为tidying。
      stop状态,线程中执行的任务为空,转为tidying。
    • termined:线程池彻底终止。
      tidying状态,执行完terminated()后,转为terminated状态
      任务数指的是线程数

    执行规则:核心线程>阻塞队列>非核心线程>拒绝策略

    • 如果任务队列为无界队列
      线程数<核心线程数:创建核心线程
      核心线程数<线程数<最大线程数:加入队列
      线程数>最大线程数:加入队列
      无界队列,创建核心线程->队列
    • 如果任务队列为有界队列
      线程数<核心线程数:创建核心线程
      核心线程数<线程数<最大线程数:加入队列,队列满后,创建非核心线程
      线程数>最大线程数:队列未满,加入队列;队列满后,拒绝策略
      有界队列,创建核心线程->队列->创建非核心线程
      任务完成后,闲置时间达到超时时间后,非核心线程会被清除
    • 如果任务队列为无缓存队列SynchronousQueue
      线程数<核心线程数:创建核心线程
      核心线程数<线程数<最大线程数:创建非核心线程
      线程数>最大线程数:拒绝策略
      无缓存队列,创建核心线程->创建非核心线程
      任务完成后,闲置时间达到超时时间后,非核心线程会被清除

    非核心线程也可以执行阻塞队列的任务

    实例demo可参考:
    https://blog.csdn.net/qq_25806863/article/details/71126867

    相关文章

      网友评论

          本文标题:线程池

          本文链接:https://www.haomeiwen.com/subject/kkeuwctx.html