zoukankan      html  css  js  c++  java
  • 线程池

    1. 结构概述

    1.1 整体结构

            

    1.2 分层描述

    1.2.1 Executor(顶级接口)

      定义执行任务的方法;

      执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程

      

    1.2.2 ExecutorService(子接口)

      定义停止任务提交任务的方法;

      提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。

      

    1.2.3 AbstractExecutorService(实现接口的抽象类)

       提供 ExecutorService 执行方法的默认实现。

      

    1.2.4 ThreadPoolExecutor(线程池具体的实现类)

      定义创建线程池的具体方法及其他细节。

      一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。

      线程池可以解决两个不同问题:

        由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能;

        并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。

      

    1.2.5 Executors(使用线程池的工具类)

      定义了线程池的一些实用方法和工厂方法;

      

     2. 基础理论(生产者+消费者)

    2.1 构造方法

        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();
            // 核心线程数。
            // 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
            // 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
            this.corePoolSize = corePoolSize;
            // 最大线程数,它表示在线程池中最多能创建多少个线程;
            this.maximumPoolSize = maximumPoolSize;
            // 一个阻塞队列,用来存储等待执行的任务。
            this.workQueue = workQueue;
            // 表示线程空闲时最多存活多长时间。
            // allowCoreThreadTimeOut为false时,keepAliveTime参数对核心线程不起作用
            // allowCoreThreadTimeOut为true时,keepAliveTime参数对所有线程(包括核心线程)都起作用
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            // 线程工厂,主要用来创建线程;
            this.threadFactory = threadFactory;
            // 表示当拒绝处理任务时的策略
            this.handler = handler;
        }

     2.2 基本属性

    private final BlockingQueue<Runnable> workQueue;              // 任务缓存队列,用来存放等待执行的任务
    
    private final ReentrantLock mainLock = new ReentrantLock();   // 线程池的主要状态锁,对线程池状态(比如线程池大小runState等)的改变都要使用这个锁
    
    private final HashSet<Worker> workers = new HashSet<Worker>();  // 用来存放工作集
     
    private volatile long  keepAliveTime;    // 线程存活时间 
      
    private volatile boolean allowCoreThreadTimeOut;   // 是否允许为核心线程设置存活时间
    
    private volatile int   corePoolSize;     // 核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
    
    private volatile int   maximumPoolSize;   // 线程池最大能容忍的线程数
     
    private volatile int   poolSize;       // 线程池中当前的线程数
     
    private volatile RejectedExecutionHandler handler; // 任务拒绝策略
     
    private volatile ThreadFactory threadFactory;   // 线程工厂,用来创建线程
     
    private int largestPoolSize;   // 用来记录线程池中曾经出现过的最大线程数
     
    private long completedTaskCount;   // 用来记录已经执行完毕的任务个数

    2.3 线程池状态

    volatile int runState;
    static final int RUNNING    = 0;
    static final int SHUTDOWN   = 1;
    static final int STOP       = 2;
    static final int TERMINATED = 3;

      runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;

      下面的几个static final变量表示runState可能的几个取值。

      当创建线程池后,初始时,线程池处于RUNNING状态;

      如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

      如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;

      当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

    2.4 线程池初始化

      默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

      在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:

      

    2.5 任务缓存队列及排队策略

      在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。

      workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

    1. ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
    2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
    3. synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

    2.6 任务拒绝策略

      当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

    1. ThreadPoolExecutor.AbortPolicy :丢弃任务并抛出RejectedExecutionException异常。
    2. ThreadPoolExecutor.DiscardPolicy :也是丢弃任务,但是不抛出异常。
    3. ThreadPoolExecutor.DiscardOldestPolicy :丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    4. ThreadPoolExecutor.CallerRunsPolicy :由调用线程处理该任务

     2.7 线程池的关闭

      ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

    • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    三. 实现原理

  • 相关阅读:
    18.11.5 考试总结
    18.11.2 考试总结
    18.11.1 考试总结
    洛谷 P1084 疫情控制 noip2013D2T3
    18.10.31 考试总结
    洛谷P1312 Mayan游戏 noip2011D1T3
    18.10.30 考试总结
    【考前复习_各类模板之补充】
    NOIP2016之反面教材提供
    【最后的抒情】【离NOIP还有9个小时】
  • 原文地址:https://www.cnblogs.com/virgosnail/p/9798104.html
Copyright © 2011-2022 走看看