为什么要用线程池
- 创建/销毁线程的系统开销过于庞大,频繁的执行这两个操作,会在很大程度上影响处理效率
- 线程并发数量过多,抢占系统资源从而导致堵塞
- 更加方便快捷的对线程进行统一管理
线程池的创建
线程池类:ThreadPoolExecutor
内含四个构造函数:
//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各参数含义:
-
int corePoolSize 该线程池中核心线程数最大值
核心线程:
在线程池新建线程时,若核心线程数小于核心线程最大值(corePoolSize),则新建的线程是核心线程,否则是非核心线程
-
int maximumPoolSize 该线程池中线程总数最大值
线程池线程总数 = 核心线程总数 + 非核心线程总数
-
long keepAliveTime 非核心线程闲置超时时长
非核心线程若是在线程池中的闲置时间超过这个时间,就会自动销毁
-
TimeUnit unit 一个枚举单位
与上面的keepAliveTime互动
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
-
BlockingQueue workQueue 线程池中的任务队列
线程池中的任务队列: 维护等待执行的Runnable对象
当所有的核心线程都在执行的时候,新建的任务会被存放在任务队列中,等待核心线程的执行;若任务队列满了,则会新建非核心线程执行任务(也就不插入到任务队列啦)
-
ThreadFactory threadFactory 线程创建的方式
主要就是给线程起名字
-
RejectExecutionHandler handler 任务拒绝策略
下文有专门的讲的地方
线程池如何执行的
-
默认情况下,线程池中没有线程,除非用了一些特定方法,预创建了一些线程(prestartAllCoreThreads()等)
-
任务出现的时候,当**线程(核心线程)数量未达到CorePoolSize的时候,则新建一个线程(核心线程)**执行任务
ps:如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都属于空闲状态,也要求创建新的线程来处理被添加的任务
-
线程(核心线程)数量达到corePookSize的时候,将任务移入任务队列,等待核心线程空出来,在依次执行任务队列中的任务
-
任务队列已经满了的话,任务就不等核心线程来处理啦,而是由线程池新建一个**线程(非核心线程)**来处理这个任务
-
队列已满,线程总是也达到了maximumPoolSize,抛出
异常(RejectedExecutionHandler handler)
如何在线程池中添加任务
很简单,一句代码就OK了
ThreadPoolExecutor.execute(Runnable r);
任务拒绝策略
产生原因
线程池的任务已经完全满了,即线程池中所有线程(核心+非核心)已经达到最大数量,并全部在进行工作,任务队列也已经排满了,那么此时,再有任务execute到线程池的时候,我们需要拒绝任务。
拒绝策略(一般四种)
-
默认方法(defaultHandler):AbortPolicy
抛弃新任务 并 抛出RejectedExecutionException异常
-
DiscardPolicy:
也是丢弃任务但是不抛出异常
-
DiscardOldestPolicy:
丢弃队列最前面的任务,然后重新尝试执行此任务(重复此过程)
-
CallerRunsPolicy:
由调用线程执行此任务