Java线程池详解

Java线程池详解

线程池解释

线程池采用了池化思想,能够有效的管理线程的生命周期,减少了每次获取资源的消耗,提高了资源的利用率。类似池化实现还有数据库连接池、HTTP连接池等

好处

  • 减少了线程创建和销毁的开销
  • 提高了响应速度
  • 使得线程更加方便管理

常见使用场景

  • 量大处理时间较短的任务:有效利用线程池减少线程创建销毁的消耗
  • 需要限制线程数量时:线程越多对资源的消耗越大,线程池可以设置最大线程数进行限制
  • 异步执行一批不需要立刻反馈结果的任务:异步执行任务,减少系统响应时间

线程池的引入:Executor框架

Executor框架为Java 5 引入,将传统线程的操控方法进行优化升级,使其针对不同 场景更加的灵活、管理更加的方便,其核心jar包为java.util.concurrent,简称JUC。此外,很重要的一点,该框架还避免了 this逃逸问题。

this逃逸:指在构造函数返回之前,其他线程持有了该对象的引用,此时引用该对象调用其方法时,可能会出现不好排查的异常。

Executor框架主要包括三个部分

  • 任务:Runnabale/Callable,可以被 ThreadPoolExecutorScheduledThreadPoolExecutor(继承ThreadPoolExecutor) 执行

  • 执行:通过实现Executor接口的子接口ExecutorService去构造相对来说比较完整的线程池执行系统

  • 返回值:线程池的优势之一,通过实现Futrure接口的FutureTask 类将异步执行的结果获取到

  • 主线程创建 Runnable 或者 Callable 的任务对象,然后把 实现的 Runnbale 或者 Callable 交给 ExecutorService 执行:ExecutorService.execute(Runnable command) 或者 把 Runnable对象或者 Callable 对象提交给 ExecutorService 执行(ExecutorService.submit(Runnable command)

  • 如果执行 ExecutorService.submit()ExecutorService 返回一个实现了 Future 接口的对象(submit() 会返回一个 FuturesTask对象,FutureTask 实现了 Runnable,可以创建FutureTask,然后直接交给 ExecutorService执行;execute 则会把异常打印出来)

  • 最后,主线程执行 FuturesTask.get() 方法等待任务执行完成。也可以通过 FutureTask.cancel(boolean mayInterruptIfRunning) 来取消此任务执行。

ThreadPoolExecutor为例,ThreadPoolExecutor继承了AbstractExecutorService(抽象函数)的submit方法,并且实现了AbstractExecutorService来自Executor接口的execute方法,因此在调用线程池的submit方法时就会通过ThreadPoolExecutorexecute将任务(Runnabale/Callable为核心的RunnableFuture对象)加到工作队列中(addWorker方法实现),返回值则通过RunnableFuture对象的形式返回。

简单来说就是主线程将Runnabale/Callable对象通过submit方法提交给线程池,线程池通过内部调度按照不同的策略执行多线程任务,然后通过返回的Futrure对象的get方法取出执行结果。

ThreadPoolExecutor 类

线程池实现类 ThreadPoolExecutorExecutor 框架最核心的类

主线程提交任务后

  • 第一步,判断核心线程池线程数是否已满,若未满则创建线程,若已满则进入第二步
  • 第二步,判断等待队列是否已满,若未满则加入队列,若已满则进入第三步
  • 第三步,判断最大线程数是否已满,若未满则创建线程,若已满则根据设定的拒绝策略处理
参数介绍:
  • corePoolSize:核心线程数,能第一时间处理的线程数
  • maximumPoolSize:最大线程数,当核心线程池以及队列都满了的时候,线程池就会扩充到最大线程数
  • workQueue: 当核心线程池已满时,队列可以用来暂存后边进来的任务
  • keepAliveTime:线程数量大于核心线程数量时,线程闲置时间超过此值的线程会被回收掉,直至缩到核心线程数
  • unitkeepAliveTime 参数的时间单位
  • threadFactory:线程工厂,executor 创建线程时用到
  • handler:拒绝策略线程池已达到最大线程数,此时队列也满了,再有任务进来就会触发拒绝策略
拒绝策略:
  • AbortPolicy:拒绝任务并抛异常RejectedExecutionException
  • CallerRunsPolicy:直接在调用线程中执行任务,影响性能
  • DiscardPolicy:拒绝任务,不报异常,不做任何处理
  • DiscradOldestPolicy:拒绝掉最早的任务,执行最新的

线程池相关的内容有很多,该文章重在介绍,若要深究其中的某个元素,可以通过参考源码的方式,这样可以最直观的看到内部组成以及各个元素之间如何合作解决线程池的实现问题。