iltonmi's docs

Java线程池

Java 线程池

重点关注几个问题

  • 线程池中线程的生命周期

    • 线程池启动后,如何初始化工作线程
    • 线程数如何变化
    • 如何管理空闲的线程
    • 提交任务给工作线程、到工作线程完成任务的过程
  • 线程池内异常处理?

  • 线程池的启动和关闭

ThreadPoolExecutor

概览

  • 线程池解决了什么问题:

    1. 执行大量异步任务时性能比较好,通过复用线程池中的线程,来避免创建和销毁线程的开销。
    2. 提供管理和限制资源的手段,比如限制线程个数或者动态新增线程。
  • 基于生产者消费者模型

  • AtomicIntger类型变量ctl表示线程池状态和线程数,最高3位为是状态,低29位是线程个数。

  • 消费者线程封装在内部类Worker中,使用HashSet类型的变量workers保存

Worker

  • Worker类实现了Runnable和AQS,run()方法调用了ThreadPoolExecutor的runWorker方法。

    /**
    * Creates with given first task and thread from ThreadFactory.
    * @param firstTask the first task (null if none)
    */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        //初始化的时候会将this引用传入工厂方法
        this.thread = getThreadFactory().newThread(this);
    }
    
    /** Delegates main run loop to outer runWorker. */
    public void run() {
        runWorker(this);
    }
    

重要的方法

execute

  • execute(Runnable command)方法,负责提交任务到阻塞队列、并在有必要的情况下添加核心线程或者普通线程。
    • 在线程数小于核心线程数时,添加核心线程addWorder(Runnable firstTask, boolean core);添加成功则返回。
    • 提交任务到阻塞队列、如果提交失败则新增线程、新增线程失败则执行拒绝策略
  • submit()是用FutureTask封装了Runnable和Callable,然后FutureTask实现了RunnableFuture,所以能够提交给execute()方法
    • 而FutureTask会通过Executors.RunnableAdapter这个适配器类包装Runnable成为Callable,在call()方法调用run()方法,run()方法执行结束后,返回传入的result。

Worker相关

  • addWorker()方法,负责增加线程数,将新的Worker添加到workers
    • 通过CAS增加线程数,直到增加成功或者检测到线程数达到最大线程数而返回false
    • 增加线程成功后,获取全局锁this.mainLock后线程安全地创建Worker实例,并且调用w.thread.start()启动Worker内部封装的线程开始执行任务。
  • runWorker()方法,在循环里,获取初始任务或者通过阻塞获取任务,获取任务后在Worker内部锁的保护下执行任务。
    • 若初始任务不为null,则执行;否则,getTask()从阻塞队列中阻塞式获取任务,若返回任务为null则退出循环。
    • w.lock()在任务执行期间加锁,是为了避免在任务运行期间,其他线程调用shutdown()后正在执行任务的线程被中断(shutdown()只会中断当前被阻塞挂起的线程)

关闭线程池

  • shutdown()方法,被调用后,线程池不会再接受新的任务、但是队列里面的任务还是会完成。
    • 设置线程池的状态为SHUTDOWN
    • 设置空闲线程的中断标志。
    • 设置中断标志前会尝试获取Worker的内部锁,因此不会中断正在执行任务的线程。
  • tryTerminate()方法,在满足条件的情况下,设置状态为TERMINATED并唤醒阻塞在termination条件队列的全部线程
    • SHUTDOWN、无工作线程、工作队列为空
    • STOP、无工作线程
  • shutdownNow()方法,线程池不会再接受新的任务,中断正在执行的任务,返回被丢弃的工作任务队列。
    • 直接设置线程池的状态为STOP
    • 中断所有线程。
    • 获取工作队列中的任务并返回。
  • awaitTermination(long timeout, TimeUnit unit),阻塞调用线程直到线程池状态变为TERMINATED。
    • 状态变为TERMINATED的2种方式:
      1. shutdown()方法,返回前调用tryTerminate()尝试设置状态为TERMINATED
      2. runWorker()方法,当工作线程运行结束后,会调用processWorkerExit()方法,也会调用tryTerminate()方法尝试设置状态为TERMINATED

ScheduledThreadPoolExecutor

  • 可以在指定延迟时间、定时进行任务调度执行的线程池

run()流程

  1. 判断是否重复执行的任务
  2. 否,则直接调用run()方法。
  3. 是,则:
    1. 调用FutureTask的runAndReset()方法
    2. setNextRunTime()设置任务下次运行的时间
    3. 调用reExecutePeriodic()方法,将任务重新放入——延迟工作队列。

DelayedQueue的take()原理

基于堆实现

  1. 先获取锁,进入循环,然后获取堆顶的任务。
    1. 如果堆顶任务为空,则在条件队列上等待。
  2. 获取任务成功,则判断任务是否到了执行时间。
    1. 如果到了,直接出队。
  3. 如果时间没到,判断自己是否leader线程。
    1. 不是的话,则在条件队列上等待。
  4. 是leader线程,则调用awaitNanos()超时自动唤醒。
    1. 唤醒后,将leader线程设置为空。
    2. 进入下一次循环。
  5. 退出循环后,用signal()唤醒等待队列中一个等待的线程,释放锁。

posted on 2021-01-25 20:56  iltonmi  阅读(87)  评论(0编辑  收藏  举报

导航