目录
一、线程池概念
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
二、线程池状态
- RUNING,当创建线程池后,初始时,线程池处于RUNNING状态;
- SHUTDOWN,如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
- STOP,如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
- TIDYING,所有任务都已终止,待调用terminated()方法;
- TERMINATED,当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
三、Excecutors四种创建线程池方法
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2.newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3. newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4.newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
四、Java中的ThreadPoolExecutor类
ThreadPoolExecutor的构造函数:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
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);
参数介绍:
- corePoolSize:核心池大小,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
- maximumPoolSize:线程池最大线程数,超过这个数后的线程将被阻塞;
- keepAliveTime:线程存活时间,如果线程数大于corePoolSize,则多出来的线程会先没有任务后指定时间关闭。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用;
- unit:参数keepAliveTime的时间单位;
- workQueue:阻塞队列,用来存储等待执行的任务;
- threadFactory:线程工厂,主要用来创建线程;
- handler:表示当拒绝处理任务时的策略。
其中,workQueue类型为BlockingQueue<Runnable>,BlockingQueue类型可以为:
- ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
- LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小;
- synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
其中,handler任务拒绝策略包括:
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常;
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务;
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。
其中,corePoolSize、maximumPoolSize、workQueue关系:
- 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程;
- 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行;
- 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务;
- 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理;
- 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程;
- 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭。
线程池执行流程:
五、线程执行流程
1、如果线程池当前线程数量少于corePoolSize,则addWorker(command, true)创建新worker线程,如创建成功返回,如没创建成功,则执行后续步骤;addWorker失败的原因可能是:
- 线程池已经shutdown,shutdown的线程池不再接收新任务。
- workerCountOf(c) < corePoolSize 判断后,由于并发,别的线程先创建了worker线程。
2、如果线程池还在running状态,将task加入workQueue阻塞队列中,如果加入成功,进行double-check,如果加入失败(可能是队列已满),则执行后续步骤;double-check主要目的是判断刚加入workQueue阻塞队列的task是否能被执行。
- 如果线程池已经不是running状态了,应该拒绝添加新任务,从workQueue中删除任务。
- 如果线程池是运行状态,或者从workQueue中删除任务失败(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务。
3、如果线程池不是running状态 或者 无法入队列,尝试开启新线程,扩容至maxPoolSize,如果addWork(command, false)失败了,拒绝当前command。
六、一个简单的线程池实现
// 通用线程池接口
public interface ThreadPool<Job extends Runnable> {
/**
* 任务执行
* @param job
*/
void excecute(Job job);
/**
* 关闭线程池
*/
void shutdown();
/**
* 增加工作线程
* @param num
*/
void addWorkers(int num);
/**
* 去掉工作线程
* @param num
*/
void removeWorkers(int num);
/**
* 获得任务数
* @return
*/
int getJobSize();
}
// 线程池的一种实现
public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
private static final int DEFAULT_SIZE = 10;
private final LinkedList<Job> jobs = new LinkedList<>();
private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());
private AtomicLong threadNum = new AtomicLong();
public void excecute(Job job) {
synchronized(jobs) {
jobs.addLast(job);
job.notify();
}
}
public void shutdown() {
for(Worker worker : workers) {
worker.shutdown();
}
}
public void addWorkers(int num) {
synchronized(jobs) {
// 此处需要数量校验
for(int i=0;i<num;i++) {
Worker worker = new Worker();
Thread t = new Thread(worker);
threadNum.incrementAndGet();
t.start();
}
}
}
public void removeWorkers(int num) {
synchronized(jobs) {
// 此处需要数量校验
for(int i=0;i<num;i++) {
Worker worker = workers.get(i);
if(workers.remove(worker)){
worker.shutdown();
}
}
}
}
@Override
public int getJobSize() {
return jobs.size();
}
class Worker implements Runnable {
private volatile boolean running = true;
public void run() {
while(running) {
Job job = null;
synchronized(jobs) {
while(jobs.isEmpty()) {
try{
jobs.wait();
} catch (Exception e) {
}
}
job = jobs.removeFirst();
}
if(job != null) {
job.run();
}
}
}
public void shutdown() {
running = false;
}
}
}
参考文章:
https://www.cnblogs.com/zhujiabin/p/5404771.html