使用线程池的好处

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

    为什么用线程池不用Thread 的 start

  1. 使用线程池的好处
  2. 有助于避免this逃逸问题
    • 线程的构造函数还没返回之前其他线程就持有了这个对象,从而调用了还没有构造完全的对象的方法

线程池底层原理与使用

image.png
上图可知,最后调用已创建好线程的对象是ExecutorService(ThreadPoolExecutor是他的实现类AbstractExecutorService的子类)

  1. 首先创建Runnable 或者 Callable 接口的任务对象。
  2. 把创建完成的实现 Runnable/Callable接口的 对象直接交给 ExecutorService(ThreadPoolExecutor对象)执行(这个过程就是把任务提交到线程池当中)
    1. .submit() 执行有返回值 返回一个futureTask对象,用来封装异步执行的结果
    2. .execut()执行返回没有返回值
  3. 主线程可以执行 FutureTask.get()方法来等待任务执行完成。也可以使用FutureTask.cancel(boolean 是否执行)来取消任务执行

ThreadPoolExecutor

线程池实现类 ThreadPoolExecutor 是 Executor 框架最核心的类。也是集团建议使用的线程池类
ThreadPoolExecutor最重要的参数(前三个最重要)
详情查看面经多线程43题前后的几题都要掌握

ThreadPoolExecutor 原理分析
线程池收到提交来的任务

  • 判断核心线程池是否已满
    • 没满创建线程执行任务
    • 满了查看等待队列是否已满
      • 等待队列没满,加入等待队列
      • 满了查看线程池满了没有
        • 没满创建线程
        • 满了按照拒绝策略处理
  • 主要方法 addWorker:用来创建新的工作线程
  • 主要方法runWorker:while循环带哦用getTask方法从workerQueue里读取任务,然后执行任务。只要getTask方法不返回null,就一直不结束
  • 主要方法getTask:
    • 如果当前线程数大于corePoolSize,会调用workQueue的poll方法获取任务
      • 如果从队列获取任务时间超过了最大生存时长,也会返回null
    • 如果当前线程数小于corePoolSize,会调用workQueue的take方法阻塞在当前

countDownLatch原理

countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
三个方法

  1. await():调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
  2. await(long timeout, TimeUnit unit) 和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
  3. countDown() :将count值减1