[TOC]
# 线程池的实现原理
## 4种实现线程池的方式
有4种实现线程池的方式`newFixedThreadPool`、`newCachedThreadPoo`l、`newSingleThreadExecutor`、`newScheduledThreadPool`;
`newFixedThreadPool`:创建一个指定线程数的线程池,当corePoolSize == maximumPoolSize时,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。
`newCachedThreadPool`:初始化一个可以缓存线程的线程池,使用SynchronousQueue作为阻塞队列,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,
当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;需控制并发任务数,否则会创建大量的线程
`newSingleThreadExecutor`:初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。
`newScheduledThreadPool`:初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
前三种实现方式 new TreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
corePoolSize:线程池中核心线程数;
maximumPoolSize:线程池中允许的最大线程数;
keepAliveTime:线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;
TimeUnit: keepAliveTime的单位;
workQueue:阻塞队列,有四种结构的阻塞队列
(1)ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
(2)LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
(3)SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
(4)priorityBlockingQuene:具有优先级的无界阻塞队列;
## threadFactory:创建线程的工厂
handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略,也可以自定义饱和策略,如记录日志或持久化存储不能处理的任务
(1)AbortPolicy:直接抛出异常,默认策略;
(2)CallerRunsPolicy:用调用者所在的线程来执行任务;
(3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
(4)DiscardPolicy:直接丢弃任务
任务提交:两种提交方式Executor.execute()、ExecutorService.submit()
`void Executor.execute()`:必须实现Runnable接口,没有返回值,无法判断任务是否执行成功;
`<T> Future<T> ExecutorService.submit()`:可以获取任务执行完的返回值。
## Executor.execute()线程池处理任务过程:
(1)获取线程池中当前线程池数量,如果当前线程池数量小于coolPoolSize,则创建新的线程执行任务;
(2)如果线程池处于running状态,并且提交的任务成功放到了阻塞队列中,则执行步骤(3),否则执行步骤(4);
(3)再次检查线程池的状态,如果线程池状态不是running,并且成功将任务移除阻塞队列,则执行reject方法处理任务
(4)创建新的线程执行任务,如果执行失败,执行reject方法处理任务
## 线程池是怎样实现创建新线程并执行任务的:
(1)判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回;
(2)判断当前需要创建的线程是否是核心线程,如果是核心线程,且线程数小于coolPoolSize,则开始创建新线程;
(3)开始创建线程:线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并调用start()方法启动Woker中的线程,执行runWorker方法。
(4)runWorker方法是线程池的核心:
a.通过unlock方法释放锁;
b.获取第一个任务firstTask,执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁;
c.firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果阻塞队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;
d.当队列中有任务加入时,线程被唤醒,并返回任务去执行,如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
## ExecutorService.submit()线程池处理任务过程:
(1)在实际业务场景中,Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果
```
private ExecuteService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args){
Future<String> future = executor.submit(new Task());
String result = future.get();
}
static class Task implements Callable<String>{
@override
public String call(){
return "success";
}
}
```
(2)Callable接口类似于Runnable,只是Runnable没有返回值。
(3)Callable任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即Future可以拿到异步执行任务各种结果;
(4)Future.get方法会导致主线程阻塞,直到Callable任务执行完成;