Java 深入理解线程池

举报
knightaa 发表于 2021/04/18 22:54:53 2021/04/18
【摘要】 一、Java 中的线程池1. 线程池状态ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量    // runState is stored in the high-order bits    private static final int RUNNING    = -1 << COUNT_BITS;    private static final...

一、Java 中的线程池
1. 线程池状态

ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    1
    2
    3
    4
    5
    6

在这里插入图片描述

线程池状态和线程池中线程的数量由一个原子整型ctl来共同表示

    使用一个数来表示两个值的主要原因是:可以通过一次CAS同时更改两个属性的值

    // 原子整数,前3位保存了线程池的状态,剩余位保存的是线程数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
    //去掉前三位保存线程状态的位数,剩下的用于保存线程数量
    private static final int COUNT_BITS = Integer.SIZE - 3;

    // 2^COUNT_BITS次方,表示可以保存的最大线程数
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    1
    2
    3
    4
    5
    6
    7
    8

获取线程池状态、线程数量以及合并两个值的操作

    // Packing and unpacking ctl
    
    // 传入 ctl 值 获取运行状态 该操作会让除高3位以外的数全部变为0
    private static int runStateOf(int c)     { return c & ~CAPACITY; }

    // 传入 ctl 值 获取运行线程数  该操作会让高3位为0
    private static int workerCountOf(int c)  { return c & CAPACITY; }

    // 传入 rs 运行状态 wc 线程数量 计算ctl新值
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

2. 线程池主要属性参数

    //阻塞队列,用于存放来不及被核心线程执行的任务
    private final BlockingQueue<Runnable> workQueue;
    
    // 全局锁,解决创建销毁线程等线程安全问题
    private final ReentrantLock mainLock = new ReentrantLock();

    // 用于存放核心线程的容器,只有当持有锁时才能够获取其中的元素
    private final HashSet<Worker> workers = new HashSet<Worker>();

    //线程工厂,给线程取名字
    private volatile ThreadFactory threadFactory;

    // 拒绝执行处理器 处理拒绝策略
    private volatile RejectedExecutionHandler handler;

    // 救急线程(或者核心线程)空闲时的最大生存时间
    private volatile long keepAliveTime;

    // 核心线程数
    private volatile int corePoolSize;

    // 最大线程数 
    // 最大线程数 - 核心线程数 = 九级线程数
    private volatile int maximumPoolSize;

    // 默认拒绝策略
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    corePoolSize : (核心线程数量),如果调用了线程池的 prestartAllCoreThreads( ) 方法,线程池会提前创建并启动所有基本线程,否则是懒惰创建
    workQueue:用于保存等待执行的任务的阻塞队列。可以选择以下几个具体实现
        ① ArrayBlockingQueue:是一个基于数组的有界阻塞队列,按FIFO(先进先出原则)排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。
        ② LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。吞吐量通常要高于ArrayBlockingQueue 。静态工厂方法 Executors.newFixedThreadPool( ) 使用了该队列
        ③ SynchronousQuene 是一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene ,静态工厂方法 Executors.newCachedThreadPool() 用的是此队列
        ④ PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
    maximumPoolSize:线程池最大线程数量,包括了核心线程数量和救急线程数量
    threadFactory:线程工厂,可以给线程设置名字等
    handler:拒绝执行处理器 处理拒绝策略 在处理过程中具体讲解
    keepAliveTime:线程活动保持时间,线程池工作线程空闲后,保持存活的时间,所以,如果任务很多,并且每个任务执行时间很多,可以调大存活时间,提高线程利用率
    unit:空闲线程存活时间单位

3. 线程池的实现原理
3.1 ThreadPoolExecutor 线程池主要处理流程

在这里插入图片描述

在这里插入图片描述

    使用者 发布任务
    如果当前运行的线程少于 核心线程数(corePoolSize),则创建新线程来执行任务(这一步需要获得全局锁,不然会引发线程安全问题)
    如果运行的线程等于或者大于corePoolSize 则将任务加入阻塞队列(BlockQueue)
    如果BlockQueue 已满,无法将任务加入队列,则创建新线程来处理任务(这同样需要获得全局锁)
        此处就用到救急线程,其数量就是最大线程数减去核心线程数的数量
    如果创建新线程使当前运行的线程超出maximumPoolSize 任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法

        拒绝策略 jdk 提供了 4 种实现
        在这里插入图片描述

        AbortPolicy:让调用者抛出 RejectedExecutionException 异常,这是默认策略

        CallerRunsPolicy:让调用者运行任务

        DiscardPolicy:放弃本次任务

        DiscardOldestPolicy:放弃队列中最早的任务,本任务取而代之
————————————————
版权声明:本文为CSDN博主「A.iguodala」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48922154/article/details/115681974

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。