欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

[并发编程] - Executor框架#ThreadPoolExecutor源码解读01

发布时间:2025/3/21 编程问答 35 豆豆
生活随笔 收集整理的这篇文章主要介绍了 [并发编程] - Executor框架#ThreadPoolExecutor源码解读01 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

  • Pre
  • Thread
    • Java线程与OS线程
    • 生命状态
    • 状态切换
  • 线程池
    • why
    • use case
    • Advantage
  • Executor框架
  • ThreadPoolExecutor 源码分析
    • 高三位低29位
    • ctl相关方法
    • 线程池存在5种状态


Pre

Java-Java中的线程池原理分析及使用


Thread

线程是调度CPU资源的最小单位,线程模型分为KLT模型与ULT模型。

Java线程与OS线程

JVM使用的KLT模型,Java线程与OS线程保持1:1的映射关系,也就是说有一个java线程也会在操作系统里有一个对应的线程 。

[并发编程] - 操作系统底层工作原理

使用new Thread 创建500个线程

public static void main(String[] args) {for (int i = 0; i < 500; i++) {new Thread(() -> {while (true){try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}

观察OS的线程数量的增长情况 。

,停止后,再观察其回落的状况

验证了 JVM使用的KLT模型,Java线程与OS线程保持1:1的映射关系。


生命状态

  • NEW 新建
  • RUNNABLE 运行
  • BLOCKED 阻塞
  • WAITING 等待
  • TIMED_WAITING 超时等待
  • TERMINATED 终结

状态切换


线程池

why

[并发编程] - 操作系统底层工作原理 中 【CPU运行安全等级】部分中说明了从用户态切换到内核态实际上是一个非常重型的操作。

如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率 ,可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。

线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。


use case

  • 单个任务处理时间比较短
  • 需要处理的任务数量很大

Advantage

  • 重用存在的线程,减少线程创建,消亡的开销,提高性能
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资
    源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

Executor框架

Executor接口是线程池框架中最基础的部分,定义了一个用于执行Runnable的execute方法。

比较常见的几个类的关系如下

Executor接口定义了唯一的接口方法

void execute(Runnable command);

Executor下有一个重要子接口ExecutorService,其中定义了线程池的具体行为

ExecutorService extends Executor

  • submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future
    对象
  • shutdown():在完成已提交的任务后封闭办事,不再接管新任务,
  • shutdownNow():停止所有正在履行的任务并封闭办事。
  • isTerminated():测试是否所有任务都履行完毕了。
  • isShutdown():测试是否该ExecutorService已被关闭。

ThreadPoolExecutor 源码分析

ThreadPoolExecutor 继承 AbstractExecutorService ,而 AbstractExecutorService 实现了ExecutorService 接口。

高三位低29位

在Java中,一个int占据32位, 使用了Integer类型来保存,高3位保存runState,低29位保存workerCount

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 用多少二进制位表示线程数量private static final int COUNT_BITS = Integer.SIZE - 3;// 线程最大数量 COUNT_BITS 就是29,CAPACITY就是1左移29位减1(29个1) 约5亿private static final int CAPACITY = (1 << COUNT_BITS) - 1;

先看下这个ctl , 通过 ctlOf(RUNNING, 0) 可以知道 ctlOf这个方法中包含的两个参数信息 : 线程池的运行状态 runState + 线程池中有效线程的数量 workerCount 。

COUNT_BITS : 29

// 高三位表示 线程池状态 // runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS;

RUNNING 表示线程池处于 运行状态,COUNT_BITS 是 29,因此这个位运算就表示 -1 左移 29 位。

-1 如用 2 进制表示

获取 -1 的正数,也就是 1 的二进制: 0000000…00000 1 (前面 31 位 0)

对上一步进行取反, 1111111111…1111 0 (前面 31 位 1)

对上一步 +1 操作, 111111111…1111 (32 位 1)

因此 - 1 左移 29 位, 就得到了 111 0000…00000 ( 29个 0) 。 高三位 111 表示 RUNNING 状态

同理

// 高三位为 000 private static final int SHUTDOWN = 0 << COUNT_BITS; // 高三位为 001 private static final int STOP = 1 << COUNT_BITS; // 高三位为 010 private static final int TIDYING = 2 << COUNT_BITS; // 高三位为 011 private static final int TERMINATED = 3 << COUNT_BITS;


ctl相关方法

// Packing and unpacking ctl// 获取运行状态private static int runStateOf(int c) { return c & ~CAPACITY; }// 获取活动线程数private static int workerCountOf(int c) { return c & CAPACITY; }// 获取运行状态和活动线程数的值private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池存在5种状态

  • RUNNING

    线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。

    线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0


  • SHUTDOWN

    线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务

    调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN


  • STOP

    线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

    调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP


  • TIDYING

    当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

    当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。


  • TERMINATED

线程池彻底终止,就变成TERMINATED状态。

线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING > TERMINATED。

进入TERMINATED的条件如下:

  • 线程池不是RUNNING状态;
  • 线程池状态不是TIDYING状态或TERMINATED状态;
  • 如果线程池状态是SHUTDOWN并且workerQueue为空;
  • workerCount为0;
  • 设置TIDYING状态成功

总结

以上是生活随笔为你收集整理的[并发编程] - Executor框架#ThreadPoolExecutor源码解读01的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。