zoukankan      html  css  js  c++  java
  • 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()唤醒等待队列中一个等待的线程,释放锁。
    不准不开心。
  • 相关阅读:
    Java——多线程常见面试题
    Java——线程定时器
    Java——多线程练习
    Java——线程池
    Java——线程间通信
    Java——线程死锁问题
    Java——线程同步
    Java多线程安全问题
    Java——多线程面试问题
    Java——多线程小例子
  • 原文地址:https://www.cnblogs.com/iltonmi/p/14327388.html
Copyright © 2011-2022 走看看