笔记,读《Java并发编程艺术》的笔记,里面夹杂了一些自己的理解,可能不是很准确,建议阅读原著。

合理使用线程池的好处

  • 降低资源消耗。降低了频繁创建和销毁造成的消耗。
  • 提高响应速度。不必等待线程创建,而是立即执行。
  • 提高线程的可管理性。

线程池的实现原理

首先判断核心线程池里是否都在执行任务。如果不是,就创建一个新的线程来执行任务;如果是,就将任务添加到队列中;如果队列满了,就在线程池中 创建一个新的线程来执行任务;如果线程池也满了,就交给饱和策略来处理任务。

从上文中,我们知道线程池里面有一个小的线程池称为核心线程池。任务先交给核心线程池来处理。

工作线程

线程池创建线程时,会将线程封装成工作线程 Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行。

线程池的创建

new ThreadPoolExecutor(int corePoolSize,
	                  int maximumPoolSize,
	                  long keepAliveTime,
	                  TimeUnit unit,
	                  BlockingQueue<Runnable> workQueue,
	                  ThreadFactory threadFactory,
	                  RejectedExecutionHandler handler)
  • corePoolSize: 核心线程池的大小。只要提交一个任务到线程池中,就创建一个新的线程,不管线程池中的其他线程是否空闲,直到线程池的线程数量大于corePoolSize 就不在创建。
  • maximumPoolSize: 真正的线程池大小。允许创建的最大线程数。只有 队列满了的时候,线程池才会创建新线程。
  • keepAliveTime: 如果线程处于休闲后,保持的存活时间。
  • unit: 线程活动保持时间的单位。
  • workQueue: 任务队列,用于保存等待执行任务的阻塞队列。有四个可以选择:
  • ArrayBlockingQueue: 基于数组结构的有界阻塞队列。
  • LinkedBlockingQueue: 基于 链表结构 的阻塞队列。
  • Executors.newFixedThreadPool()Executors.newSingleThreadExecutor() 都是使用的此队列。
  • SynchronousQueue: 不存储元素的阻塞队列。每插入一个元素就会阻塞等待另一个线程移除。
  • Executors.newCacheThreadPool() 使用的这个队列。
  • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
  • threadFactory: 用于设置创建线程的工厂,可以为每个线程设置更有意义的名字。
  • handler: 设置饱和策略,线程池满了的处理方式。有四种:
  • AbortPolicy:直接抛出异常。
  • CallerRunsPolicy:只用调用者所在的线程 来执行任务。
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • DiscardPolicy:不处理,丢弃掉。

execute()submit() 方法

  • 都是用来提交任务的。
  • execute() 没有返回值,不能够判断任务是否被线程池执行成功。
  • submit() 有返回值,返回一个 Future 对象来获取返回值。调用get()方法会阻塞当前线程直到任务完成。

关闭线程池

通过调用shutdownshutdownNow 方法来关闭线程池。

原理: 遍历线程池中的所有线程,并调用 interrupt 方法中断线程。无法响应中断任务可能永远无法终止,如被阻塞,某些线程可能会不理会 中断。

  • shutdown:将线程池设置成 SHUTDOWN 状态,然后中断没有正在执行任务的线程,等待线程都指定完毕就关闭线程池。
  • shutdownNow:会将线程池状态设置为 STOP 状态,将尝试停止所有正在执行或暂停任务的线程 。

监控线程池

监控线程池时,使用的一些属性:

  • taskCount:线程池需要执行的任务数量。
  • completedTaskCount:线程池在运行过程中已完成的任务数量。
  • largestPoolSize:线程池曾经创建过的最大线程数量。
  • getPoolSize:线程池的线程数量。
  • getActiveCount:活动的线程数。