自定义线程池总结
一、四种线程池
Java通过Executors提供四种线程池,分别为:
1、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2、newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newScheduledThreadPool
创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。
4、newCachedThreadPoo
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
在alibaba java编码规范中 是不允许使用Executors去创建的
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
具体原因 不在多叙述.
二、体系结构**
上图列举了线程池中非常重要的接口和类:
(1)Executor,线程池顶级接口;
(2)ExecutorService,线程池次级接口,对Executor做了一些扩展,增加一些功能;
(3)ScheduledExecutorService,对ExecutorService做了一些扩展,增加一些定时任务相关的功能;
(4)AbstractExecutorService,抽象类,运用模板方法设计模式实现了一部分方法;
(5)ThreadPoolExecutor,普通线程池类,这也是我们通常所说的线程池,包含最基本的一些线程池操作相关的方法实现;
(6)ScheduledThreadPoolExecutor,定时任务线程池类,用于实现定时任务相关功能;
(7)ForkJoinPool,新型线程池类,java7中新增的线程池类,基于工作窃取理论实现,运用于大任务拆小任务、任务无限多的场景;
(8)Executors,线程池工具类,定义了一些快速实现线程池的方法(谨慎使用);
3.ThreadPoolExecutor 线程池
普通线程池类,这也是我们通常所说的线程池,包含最基本的一些线程池操作相关的方法实现。
线程池的主要实现逻辑都在这里面,比如线程的创建、任务的处理、拒绝策略等.
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
ThreadPoolExecutor 提供了四个构造函数
以第四个构造函数为例 各参数意思
public ThreadPoolExecutor(int corePoolSize, // 1
int maximumPoolSize, // 2
long keepAliveTime, // 3
TimeUnit unit, // 4
BlockingQueue<Runnable> workQueue, // 5
ThreadFactory threadFactory, // 6
RejectedExecutionHandler handler ) { //7
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | coolPoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
4、自定义线程池
自定义线程池就是明白这些参数含义,明白线程池工作原理.
在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,(除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程)。
默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。
线程池按以下行为执行任务
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
- 若线程数小于最大线程数,创建线程
- 若线程数等于最大线程数,抛出异常,拒绝任务
5、自定义线程池
1、自定义阻塞队列
package com.bosssoft.hr.train.j2se.basic.example.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @program: bosssoft-train-example
* @description: 阻塞队列
* @author: rwdou
* @create: 2020-11-04 10:16
**/
@Slf4j
public class BlockQueue<T> {
//阻塞队列容量
private int capacity;
private Deque<T> deque=new ArrayDeque<>();
private ReentrantLock lock=new ReentrantLock();
/**
*
* @Description 等待条件
**/
private Condition emptyWaitSet=lock.newCondition();
private Condition fullWaitSet=lock.newCondition();
public BlockQueue(int capacity) {
this.capacity = capacity;
}
/**
* 从队列中获取数据,带超时时间
* @param timeout
* @param timeUnit
* @return
*/
public T poll(long timeout, TimeUnit timeUnit){
lock.lock();
try{
long nanos=timeUnit.toNanos(timeout);
while(deque.isEmpty()){
try {
if(nanos<=0){
return null;
}
//返回剩余需要等待的时间
nanos = emptyWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
log.info(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
}
T t=deque.removeFirst();
log.info("poll 返回 {}",t);
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
/**
* 从队列中获取数据,不带超时时间,如果队列中一直没有数据,会死等[一直等]下去
* @return
*/
public T take(){
lock.lock();
try{
while(deque.isEmpty()){
try {
emptyWaitSet.await(); //队列数据为空,需要等待其他线程向队列中存放数据
} catch (InterruptedException e) {
log.info(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
}
T t=deque.removeFirst();
log.info("take 返回 {}",t);
fullWaitSet.signal();
return t;
}finally {
lock.unlock();
}
}
/**
* 存放数据,如果队列满了会一直死等下去
* @param t
*/
public void put(T t){
lock.lock();
try{
while(deque.size()==capacity){
try {
log.info("put 等待加入 {}",t);
fullWaitSet.await();
} catch (InterruptedException e) {
log.info(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
}
log.info("put {}",t);
deque.addLast(t);
emptyWaitSet.signal();
}finally {
lock.unlock();
}
}
/**
* 存放数据,如果超过了一定时间则进入下一次循环
* @param t
*/
public boolean offer(T t,long timeout,TimeUnit timeUnit){
lock.lock();
try{
long nanos=timeUnit.toNanos(timeout);
while(deque.size()==capacity){
try {
if(nanos<=0){
return false;
}
log.info("offer 等待加入 {}",t);
nanos=fullWaitSet.awaitNanos(nanos);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
deque.addLast(t);
log.info("offer {}",t);
emptyWaitSet.signal();
return true;
}finally {
lock.unlock();
}
}
/**
* 队列满了后怎么做,让用户自己选择
* @param rejectPolicy
* @param t
*/
public void tryPut(RejectPolicy<T> rejectPolicy,T t){
lock.lock();
try{
if(deque.size()==capacity){
rejectPolicy.reject(this,t); //拒绝策略
}else {
log.info("put {}", t);
deque.addLast(t);
emptyWaitSet.signal();
}
}finally {
lock.unlock();
}
}
}
2、自定义拒绝策略
只提供接口 拒绝策略 线程池使用者自己实现 根据不同的业务场景灵活变化
@FunctionalInterface
public interface RejectPolicy<T> {
void reject(BlockQueue<T> queue,T task);
}
3、线程池 + 线程工作包装类
package com.bosssoft.hr.train.j2se.basic.example.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
/**
* @program: bosssoft-train-example
* @description: 自定义线程池
* @author: rwdou
* @create: 2020-11-04 10:30
**/
@Slf4j
public class ThreadPool {
private int coreSize; //核心线程池大小
private BlockQueue<Runnable> taskQueue; //当工作线程容量满了的时候任务放入阻塞队列
private int timeout; //超时时间
private TimeUnit timeUnit; //时间单位
private int queueCapacity; //队列容量
private RejectPolicy<Runnable> rejectPolicy; //拒绝策略
private HashSet<Worker> workers = new HashSet<>(); //存放工作者线程
public ThreadPool(int coreSize, int timeout, TimeUnit timeUnit, int queueCapacity, RejectPolicy<Runnable> rejectPolicy) {
this.coreSize = coreSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
this.queueCapacity = queueCapacity;
taskQueue = new BlockQueue<>(this.queueCapacity);
this.rejectPolicy = rejectPolicy;
}
public void execute(Runnable task) {
synchronized (workers) {
if (workers.size() < coreSize) {
Worker worker = new Worker(task);
log.info("新增worker {},{}", worker, task);
workers.add(worker);
new Thread(worker).start();
} else {
/*log.debug("加入任务队列 {}", task);
taskQueue.put(task);*/
// 1) 死等
// 2) 带超时等待
// 3) 让调用者放弃任务执行
// 4) 让调用者抛出异常
// 5) 让调用者自己执行任务
taskQueue.tryPut(rejectPolicy, task);
}
}
}
/**
* @Author rwdou
* @Description 线程池内部任务的包装类
* @Date 11:15 2020/11/4
**/
class Worker implements Runnable {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
try {
log.info("正在执行...{}", task);
task.run();
} catch (Exception e) {
log.error(e.getLocalizedMessage());
} finally {
task = null;
}
}
synchronized (this) {
log.info("worker 被移除...{}", this);
workers.remove(this);
}
}
}
}