Java线程池源码详解(一)

什么是线程池

  • 线程池可以简单理解为一组线程的集合, 由线程池来管理各个线程, 我们可以向线程池提交任务, 并由其管理的线程去执行
  • 像线程池、连接池这些池化技术, 主要通过对线程/连接进行管理, 使得线程/连接可以复用, 因为线程/连接的创建是很昂贵的(开销大), 所以通过池化技术来进行管理, 避免频繁创建销毁很有必要.
  • 创建一个线程池需要哪些组件?
    • 首先最基本的是要有线程
    • 其次, 我们需要像线程池提交任务, 所以需要有一个存放任务的容器, 比如队列
    • 有了这两个部分后, 线程就可以不断的去队列中取任务来执行
    • 这只是一个简单的模型, 但是实际中需要考虑比较多的问题:
      • 比如多个线程同时去队列取任务会不会有线程安全问题(所以这里可以用阻塞队列);
      • 当任务比较少时, 空闲的线程如何处理, 一直空闲着会占用过多资源
      • 任务队列的大小如何控制? 任务队列满了怎么办?
      • 这些问题都会在后面一一解答

线程池构成

  • 先来认识一下Java线程池的基本流程, 网上找了一张图如下
    在这里插入图片描述
  • 运行流程如下
    • 首先主线程调用线程池execute方法提交任务
    • 线程池首先判断目前已有的线程数量(workerCount)是否小于核心线程是(corePoolSize)
    • 如果workerCount<corePoolSize, 则直接创建一个新线程来执行任务, 返回
    • 如果workerCOunt>=corePoolSize, 则尝试把任务提交到任务队列
    • 如果任务队列未满, 则提交到任务队列, 返回
    • 如果任务队列已满, 则判断目前已有线程数(workerCount)是否小于线程池的最大线程数(maxPoolSize), 注意走到这一步的workerCount>=corePoolSize
    • 如果workerCount<maxPoolSize, 那么创建一个新线程执行任务, 返回
    • 否则, 把任务交由拒绝策略来处理
  • 核心线程和非核心线程:
    • Java线程池中把线程分为两类, 核心线程和非核心线程
    • 核心线程数<=总线程数, 核心线程+非核心线程=总线程数,
    • 创建线程时, 先创建的是核心线程, 当到达核心线程数后, 创建的就是非核心线程;
  • 任务队列: 这个Java是采用BlockingQueue的实现类, 因此可以用LinkedBlockingQueue、ArrayBlockingQueue等, 可按需选择
  • 拒绝策略: 当已有线程数目>=最大线程数, 且任务队列已满时, 会把任务交给拒绝策略来操作; Java线程池提供了四种拒绝策略
    • AbortPolicy: 直接丢弃任务, 并抛出异常
    • DiscardPolicy: 直接丢弃任务, 不会抛出异常
    • CallerRunsPolicy: 让提交该任务的线程来执行, 这样可能会影响任务提交速度
    • DicardOldestPolicy: 丢弃掉任务队列中的最老任务(即队头任务), 然后将该任务添加到队列
  • 一些核心参数:
    • corePoolSize: 线程池的核心线程数
    • maxPoolSize: 线程池的最大线程数, corePoolSize <= maxPoolSize
    • keepAliveTime: 非核心线程空闲时间达到keepAliveTime时, 会被销毁
    • allowCoreThreadTimeOut: 默认核心线程创建出来后是不会被销毁的, 如果该参数为true, 则核心线程空闲超过keepAliveTime后也会被销毁
    • defaultHandler: 线程池的拒绝策略
    • workQueue: 线程池的任务队列

Java中的线程池

  • Java中的线程池接口是Executor(不要和Executors混淆, Executors是线程池的一个工具类); 继承关系图如下: 在这里插入图片描述

  • 顶层接口Executor: 其代码也比较简单, 只有一个execute方法用于提交任务

    public interface Executor {
        void execute(Runnable command);
    }
    
  • ExecutorService接口: 定义了线程池的关闭(shutdown)方法, 提交任务submit方法, 以及批量任务执行方法; 具体如下

    public interface ExecutorService extends Executor {
    	//和线程池关闭相关的系列方法
        void shutdown();
        List<Runnable> shutdownNow();
        boolean isShutdown();
        boolean isTerminated();
        boolean awaitTermination(long timeout, TimeUnit unit)  throws InterruptedException;
    
    	//submit方法, 和Executor#execute不同, submit有返回值Future, 可以用来查看任务执行情况
        <T> Future<T> submit(Callable<T> task);
        <T> Future<T> submit(Runnable task, T result);
        Future<?> submit(Runnable task);
    
    	//批量执行任务, 此处不重要
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
            throws InterruptedException;
        ......
    }
    
  • AbstractExecutorService: 这个抽象类主要实现了submit()和invokeAll这两类方法; 下面摘一些主要方法实现看看

    public abstract class AbstractExecutorService implements ExecutorService {
    
    //将Runnable任务封装为一个RunnableFuture对象, 这个对象主要是提供给调用者, 
    //可以观察任务的执行完了与否或者成功与否, 以及可以拿到返回值等
       protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
           return new FutureTask<T>(runnable, value);
       }
       
       //提交任务方法
       public Future<?> submit(Runnable task) {
           if (task == null) throw new NullPointerException();
           //先把Runnable任务封装为RunnableFuture对象
           RunnableFuture<Void> ftask = newTaskFor(task, null);
           //提交执行
           execute(ftask);
           //把RunnableFuture对象返回, 这样调用者就可以通过该对象的方法查看任务执行情况
           return ftask;
       }
       
       //....其他内容省略
    
  • ThreadPoolExecutor: 这是线程池的默认实现类, 该类比较复杂, 我们分步来看

    • 内部类: 如下图, 这几个是ThreadPoolExecutor的内部类, 主要分两部分
      • Worker: 这就是线程池中的线程实例, 每一个Worker可以理解为一个线程
      • 其余四个内部类都是线程池的拒绝策略, 和前面讲的一样
        在这里插入图片描述
    • 成员方法: 这里方法就先不分析了, 比较多和杂, 后面再做分析
    • 成员变量: 主要成员变量和前面将的线程池核心参数差不多, 这里挑部分重要的成员变量
      //这个ctl通过位运算记录了当前线程数以及线程池的状态两部分信息, 可暂时不看
      private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
      //接下来这几个都是和线程池的状态有关的, 都是采用位运算方式来获取, 可暂时不看
      private static final int COUNT_BITS = Integer.SIZE - 3;
      private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
      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;
      
      //这个set用来保存创建的每一个worker, 可以理解为线程
      private final HashSet<Worker> workers = new HashSet<Worker>();
      
      //一个锁, 主要是当创建worker并添加到set时, 为保证线程安全, 必须拿到锁才能添加
      private final ReentrantLock mainLock = new ReentrantLock();
      
      //记录已完成的任务数
      private long completedTaskCount;
      
      //线程工厂, 工厂模式, 用于创建worker时创建线程
      private volatile ThreadFactory threadFactory;
      
      //这部分和前面将的核心参数是一个意思, 不在赘述
      private final BlockingQueue<Runnable> workQueue;
      private int largestPoolSize;
      private volatile RejectedExecutionHandler handler;
      private volatile long keepAliveTime;
      private volatile boolean allowCoreThreadTimeOut;
      private volatile int corePoolSize;
      

小结

  • 到这里, 我们就基本明白了Java线程池中提交任务的基本流程
  • 设计线程池的一些重要部分, 如任务队列, 线程, 一些核心参数都在上面分析ThreadPoolExecutor中有相应的源码
  • 了解了这些后, 后面我们再分析提交任务的具体代码分析
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值