线程池的优点

1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程可以重复使用;
2、可以根据系统的性能,调整工作线程数量,反正消耗过多内存导致服务器崩溃。

线程池原理

1、线程池创建核心方法
  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. RejectedExecutionHandler handler)

corePoolSize:线程池核心线程数量
maximumPoolSize:线程池最大线程数量
keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
unit:存活时间的单位
workQueue:存放任务的队列
handler:超出线程范围和队列容量的任务的处理程序

2、线程池实现原理

提交一个任务到线程池中,线程池的处理流程如下:
1、判断线程池的核心线程是否都在执行任务,如果不是(核心线程空闲或者核心线程还未创建)则创建一个新的工作线程来执行任务。核心线程都在执行任务,则进入下个阶段。
2、判断工作队列是否已满。如果队列未满,则将任务加入到队列中;如果工作队列满了,则进入下个阶段。
3、判断最大工作线程是否都在工作状态,如果不是,则创建新的工作线程执行任务;如果已经满了,则交给饱和策略来处理。

饱和策略

1、AbortPolicy:直接抛出异常
2、CallerRunsPolicy:只用调用所在的线程运行任务
3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
4、DiscardPolicy:不处理,丢弃掉。

创建线程池的方式

Executor框架类图
image.png
1、JAVA线程既是工作单元,也是执行机制。而在Executor框架中,我们将工作单元与执行机制分离开来。Runnable和Callable是工作单元(也就是俗称的任务),而执行机制由Executor来提供。这样一来Executor是基于生产者消费者模式的,提交任务的操作相当于生成者,执行任务的线程相当于消费者。
2、从类图上看,Executor接口是异步任务执行框架的基础,该框架能够支持多种不同类型的任务执行策略。

  1. public interface Executor {
  2. void execute(Runnable command);
  3. }

Executor接口就提供了一个执行方法,任务是Runnbale类型,不支持Callable类型。
3、ExecutorService接口实现了Executor接口,主要提供了关闭线程池和submit方法:

  1. public interface ExecutorService extends Executor {
  2. List<Runnable> shutdownNow();
  3. boolean isTerminated();
  4. <T> Future<T> submit(Callable<T> task);
  5. }

4、另外该接口有两个重要的实现类:ThreadPoolExecutor与ScheduledThreadPoolExecutor。
其中ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务;而ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行任务,或者定期执行命令。
5、Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadExecutor和CachedThreadPool
a、SingleThreadExecutor:单线程线程池

  1. public static ExecutorService newSingleThreadExecutor() {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>()));
  6. }

我们从源码来看可以知道,单线程线程池的创建也是通过ThreadPoolExecutor,里面的核心线程数和线程数都是1,并且工作队列使用的是无界队列。由于是单线程工作,每次只能处理一个任务,所以后面所有的任务都被阻塞在工作队列中,只能一个个任务执行。
b、FixedThreadExecutor:固定大小线程池

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }

c、CachedThreadPool:无界线程池

  1. public static ExecutorService newCachedThreadPool() {
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  3. 60L, TimeUnit.SECONDS,
  4. new SynchronousQueue<Runnable>());
  5. }

无界线程池意味着没有工作队列,任务进来就执行,线程数量不够就创建,与前面两个的区别是:空闲的线程会被回收掉,空闲的时间是60s。这个适用于执行很多短期异步的小程序或者负载较轻的服务器。