• Java 并发系列(一) ThreadPoolExecutor源码解析及理解


    ThreadPoolExecutor
    它是线程池最核心的类, 这里对核心的方法做简要的剖析(会持续更新),以加深对线程池运行原理的理解。
    1. 核心成员变量及相关方法
     1     // ctl非常重要,用整型表示,共32位,其中**高3位代表线程池状态,低29位代表工作线程数**;
     2     // 线程池状态初始化为RUNNING,工作线程数为0
     3     private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
     4 
     5     // 偏移量29
     6     private static final int COUNT_BITS = Integer.SIZE - 3;
     7 
     8     // 理论最大线程数(约500万)
     9     private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    10 
    11     // -1左偏移29位(下同),运行中状态,既能接收新提交的任务,又能执行阻塞队列中的任务
    12     private static final int RUNNING    = -1 << COUNT_BITS;
    13 
    14     // 关闭状态,不再接收新提交的任务,但还能继续执行阻塞队列中的任务(调用shutdown()可以进入此状态)
    15     private static final int SHUTDOWN   =  0 << COUNT_BITS;
    16 
    17     // 停止状态,不再接收新提交的任务,也不再执行队列中的任务;而且会尝试中断正在执行的工作线程(调用shutdownNow()可以进入此状态)
    18     private static final int STOP       =  1 << COUNT_BITS;
    19 
    20     // 清理状态,当workCount(工作线程数)为0,且队列也为空时就是此状态
    21     // SHUTDOWN -> TIDYING 线程数为0,队列也为空时会自动进入改状态
    22     // STOP -> TIDYING 线程数为0时,就会自动进入该状态
    23     private static final int TIDYING    =  2 << COUNT_BITS;
    24 
    25     // 终结状态 可以通过调用awaitTermination方法来来等待线程池彻底终结
    26     private static final int TERMINATED =  3 << COUNT_BITS;
    27 
    28     // 获取线程池运行状态;因为CAPACITY为29个1,取反后是29个0,再通过&运算会取出最高的3位
    29     private static int runStateOf(int c)     { return c & ~CAPACITY; }
    30 
    31     // 获取线程池中线程数;取出最低的29位
    32     private static int workerCountOf(int c)  { return c & CAPACITY; }
    33     
    34     // 将运行状态与线程数拼接起来,共有恰好有32位(因为rs已经左偏移29位了!)
    35     private static int ctlOf(int rs, int wc) { return rs | wc; }

    2. 核心构造方法 

     1 /*
     2 *   1. 共有7个参数
     3 *   2. 具体实现不再细说,只简单说下各个作用(就是使用流程)
     4 *   corePoolSize: 核心线程数,如果总工作线程数小于核心线程数, 有新任务时则会继续创建新的线程
     5 *   maximumPoolSize: 最大线程数,理论上包含了核心线程数和非核心线程数
     6 *   keepAliveTime: 一般上(allowCoreThreadTimeOut=false)是指非核心线程没有任务执行的存活时间(可以通过getTask()方法去分析)
     7 *   TimeUnit: keepAliveTime的时间单位
     8 *   workQueue: 阻塞队列,其中包含有SynchronousQueue, ArrayBlockingQueue, LinkedBlockingQueue; 存放核心线程执行不过来时被提交的任务
     9 *   threadFactory: 线程工厂,创建线程的地方
    10 *   handler: 拒绝策略,线程池满时会执行该策略rejectedExecution()方法;自带有4种拒绝策略,默认使用抛异常拒绝策略,另有什么都不做策略,用调用者线程执行任务策略,抛弃最旧任务策略
    11 *
    12 *   注意:1. 核心线程和非核心线程只是个逻辑的概念,某个线程被创建后,一开始可能是核心的,到后来会变成非核心的,身份并不固定。(看具体getTask()时会不会得到null)
    13 *        2. 流程归总:当新任务被提交后,当工作线程数小于核心核心线程数时,会继续创建线程来处理此任务,否则会将其放在阻塞队列中;若阻塞队列已满,则会创建线程来处理此任务;若创建线程失败(不小于了最大线程数),则会执行拒绝策略。
    14 */
    15 public ThreadPoolExecutor(int corePoolSize,
    16                               int maximumPoolSize,
    17                               long keepAliveTime,
    18                               TimeUnit unit,
    19                               BlockingQueue<Runnable> workQueue,
    20                               ThreadFactory threadFactory,
    21                               RejectedExecutionHandler handler) {
    22         if (corePoolSize < 0 ||
    23             maximumPoolSize <= 0 ||
    24             maximumPoolSize < corePoolSize ||
    25             keepAliveTime < 0)
    26             throw new IllegalArgumentException();
    27         if (workQueue == null || threadFactory == null || handler == null)
    28             throw new NullPointerException();
    29         this.acc = System.getSecurityManager() == null ?
    30                 null :
    31                 AccessController.getContext();
    32         this.corePoolSize = corePoolSize;
    33         this.maximumPoolSize = maximumPoolSize;
    34         this.workQueue = workQueue;
    35         this.keepAliveTime = unit.toNanos(keepAliveTime);
    36         this.threadFactory = threadFactory;
    37         this.handler = handler;
    38     }

    3. 疑问

    1. 线程池属于terminated后,是怎么样释放资源的?terminated()方法是空实现?
    2. execute()添加到队列后为什么还要recheck?
  • 相关阅读:
    EOJ 2743 Stock Exchange
    POJ-3468 A Simple Problem with Integers
    EOJ-1104 bitmap
    【转】旋转卡壳——凸多边形间对踵点对(定义)
    Ring 3层枚举进程的四种方法
    XX-Net项目,免费浏览谷歌的伟大项目
    浅析Java中的内存机制
    Ubuntu下eclipse中安装Scala插件
    注入(5)---导入表注入(HookINT)
    Linux下MySQL导入文件出错ERROR 1290 (HY000)
  • 原文地址:https://www.cnblogs.com/nolan4954/p/10477200.html
Copyright © 2020-2023  润新知