java线程池

发布于:2024-05-21 ⋅ 阅读:(126) ⋅ 点赞:(0)

概要

在 HotSpot VM 的线程模型中,Java 线程被一对一映射为内核线程。Java 在使用线程执行程序时,需要创建一个内核线程;当该 Java 线程被终止时,这个内核线程也会被回收。因此 Java 线程的创建与销毁将会消耗一定的计算机资源,从而增加系统的性能开销。

为了解决上述两类问题,Java 提供了线程池概念,线程池的作用总结如下:

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

线程池框架Executor

为了更有效地帮助开发人员进行多线程开发,Java 提供了一套 Executor 框架。这个框架中包括了 ScheduledThreadPoolExecutorThreadPoolExecutor 两个核心线程池。前者是用来定时执行任务,后者是用来执行被提交的任务。这两个线程池的核心原理是一样的,下面重点看看 ThreadPoolExecutor 类是如何实现线程池的。
在这里插入图片描述

Executors 实现了以下四种类型的 ThreadPoolExecutor:
在这里插入图片描述

线程池状态

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

状态名 高 3位 接收新任务 处理阻塞队列任务 说明
RUNNING 111 Y Y
SHUTDOWN 000 N Y 不接收新任务,会处理阻塞队列剩余任务
STOP 001 N N 会中断正在执行任务并抛弃阻塞队列任务
TIDYING 010 - - 任务全执行完毕,活动线程为 0 即将进入终结
TERMINATED 011 - - 终结状态

从数字上比较,TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING
这些信息存储在一个原子变量 ctl 中,目的是将线程池状态与线程个数合二为一,这样就以用一次 cas 原子操作进行赋值

用一个整数的不同位,来保存两种信息(线程的状态+线程的个数),也就是将这两种信息合并成一个cas来操作

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;

// c 为旧值, ctlOf 返回结果为新值
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))));

// rs 为高 3 位代表线程池状态, wc 为低 29 位代表线程个数,ctl 是合并它们
private static int ctlOf(int rs, int wc) { return rs | wc; }

构造函数

/**
     * corePoolSize :
     *        核心线程数大小:不管它们创建以后是不是空闲的。
     *        线程池需要保持 corePoolSize 数量的线程,除非设置了 allowCoreThreadTimeOut。
     *
     * maximumPoolSize :
     *        最大线程数:线程池中最多允许创建 maximumPoolSize 个线程
     * keepAliveTime :
     *         存活时间:如果经过 keepAliveTime 时间后,
     *         超过核心线程数的线程还没有接受到新的任务,那就回收。
     * unit :keepAliveTime 的时间单位
     * workQueue
     *          存放待执行任务的队列:当提交的任务数超过核心线程数大小后,
     *          再提交的任务就存放在这里。它仅仅用来存放被 execute 方法提交的 Runnable 任务。
     *
     * threadFactory 线程工程:用来创建线程工厂。比如这里面可以自定义线程名称,
     *
     *  handler
     *          拒绝策略:当队列里面放满了任务、最大线程数的线程都在工作时,
     *          这时继续提交的任务线程池就处理不了,应该执行怎么样的拒绝策略
    	救急线程数=maximumPoolSize -corePoolSize
        救急线程执行完后会消亡 而核心线程不会;keepAliveTime unit 这两个参数进行控制
     *
     */

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

执行流程

其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

在这里插入图片描述

队列

在这里插入图片描述

拒绝策略

在这里插入图片描述

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到