实现自己的线程池
大家里面的案例可以从gitHub下载下来自己看一下
地址:https://github.com/JolyouLu/Juc-study.git 代码在Juc-MyExecutor下
线程池的本质
在实现线程池之前我们要了解,线程池的本质是干嘛的,他解决了什么问题,在使用多线程的时候并不是线程开的越多越好,如果我们通过new Thread创建和使用的线程是不能重复利用的,频繁使用new堆容易满会加快CG(垃圾回收机制)的频率,每一次CG JVM会停止所有工作等待CG结束,使得使用多线程并没有得到理想的效果,线程池他相当于多线程的容器,线程池可让线程重复利用,并且可以控制线程的当前运行数量,等待线程数量,多余线程解决策略等问题。
线程池的比喻
我们要实现一个线程池那首先我们要知道,实现线程池需要什么,我们可以把线程池比喻做一个工厂,你是老板,你要开一条流水线那需要什么,生产原料机器,正式工数量,临时工数量,正式工状况,拒单策略
原料机器:只要原料机里面有原料就要生产,工人就需要拿到原料去组装,相当线程生产只要有线程就要执行
原料暂存区: 原料机的生产速度肯定比工人组装速度快,如果正式工满人了那么原料就会被堆放在原料区域,那 如果原料区被堆满了这是你是老板那你是否需要考虑招一些临时工了,相当线程池的队列,如果核心线程满了那么就会把任务加入队列中,如果队列满了那么将来会创建新线程执行
正式工:正式工就是长期要上班的,每工作你也要上班,相当线程池的核心容量即使任务都执行完了核心线程还是会空跑着在后台
临时工:如果突然干活工作量大而已要快,那么工厂就会招临时工,临时工与正式工不同任务干完了就会被炒鱿鱼,相当于线程池最大容量
工人状况:如果这几个正式工这几天来上班,但是没有任何工作需要做那是不是考虑把他们也炒鱿鱼,减少资金销毁
拒单策略:如果我们的原料暂存区满了,招的临时工也满了,那应该要拒绝接收新的订单停止原料,让工人把手头上的事情做完,避免原料的浪费
JDK的线程池
经过以上的比喻我们在看看JDK线程池接口
public ThreadPoolExecutor(int corePoolSize, //核心容量
int maximumPoolSize, //最大容量
long keepAliveTime, //超时时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue) { //线程队列
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
初始一个我的线程池
//我这里初始了一个 核心容量是10 最大容量是50 核心线程空闲0秒就去处 LinkedBlockingQueue队列的线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 50, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
核心容量 最大容量 线程超时相信大家都理解,那么我这里要说一个这个队列
JDK线程池的队列
ArrayBlockingQueue:创建时需要传入长度,可以限定队列的长度,接收到任务的时候,如果当前核心线程已经跑满了那么任务就会加入这个队列,如果这个队列也加满了,那么CPU会创建临时线程执行任务,如果开启的临时线程大于最大容量数那么,则会发生错误
LinkedBlockingQueue:这是个无界队列,队列的长度为Integer.MAX_VALUE,即无限大,那么如果当前核心线程已经跑满了那么任务就会加入这个队列,因为该队列是无限大的,所以永远也不会满,意思是使用该队列后,你的最大容量设置后是无意义的,因为只有队列满了CPU才会去创建临时线程执行任务,但是你的队列根本不会满
SynchronousQueue:这个同步队列,简单来说就是这个队列根本就没有长度,只是装个样子它根本就装不下任何一个任务,因为这个队列根本就没有容量,那么如果当前核心线程跑满了,这个队列又装不下东西的,那么CPU就会创建临时线程执行任务,如果开启的临时线程大于最大容量数那么,则会发生错误
DelayQueue:队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口,这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
线程池工作流程
首先我们主线程(main)会调用一个execute方法,任何我们就会去判断一些当前线程池核心容量,如果核心容量还没满那么就会调用addWork方法把任务加入到核心线程中,如果核心线程满了那么会把任务offer到我们的等待队列,等待核心线程有空位了在从队列中take出来,如果队列也加满了那么这是会创建非核心容量容器去跑这些任务,如果非核心容器也满了再有任务进来,将会使用拒绝策略
实现自己的线程池
Executor接口
所有线程池的基类,提供execute接口
public interface Executor {
void execute(Runnable task);
}
ThreadPoolExecutor实现类
因为是简单实现 所以我就没有使用那么多层继承
public class ThreadPoolExecutor implements Executor {
//核心容量
private volatile int corePollSize;
//最大容量
private volatile int maximumPoolSize;
//核心线程超时销毁
private volatile long keepAliveTime;
//描述是否需要超时销毁
private volatile boolean allowCoreThreadTimeOut;
//当前数量
private final AtomicInteger ctl = new AtomicInteger(0);
//队列
private BlockingQueue<Runnable> workQueue;
//初始线程池
public ThreadPoolExecutor(int corePollSize, int maximumPoolSize, BlockingQueue<Runnable> workQueue) {
this.corePollSize = corePollSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
}
public ThreadPoolExecutor(int corePollSize, int maximumPoolSize, long keepAliveTime, boolean allowCoreThreadTimeOut, BlockingQueue<Runnable> workQueue) {
this.corePollSize = corePollSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
if (keepAliveTime>0){
allowCoreThreadTimeOut= true;
}
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
this.workQueue = workQueue;
}
/**
* 暴露接口 接收 task任务
* @param command
*/
@Override
public void execute(Runnable command) {
if (command == null){
throw new NullPointerException();
}
int c = ctl.get();
if (c < corePollSize){ //判断是不是小于核心
addWorker(command,true); //把任务添加到核心容量
}else if (workQueue.offer(command)){ //如果核心满了 添加到队列中 成功true 失败flash
addWorker(null,false); //传空
}else {
reject(command); //如果队列也满了 使用拒绝策略
}
}
//添加任务内容
private void addWorker(Runnable task,boolean core) {
if(core){//如果true,表示使用核心容量,计数加1
ctl.incrementAndGet();
}
Worker worker = new Worker(task);//传入task任务
worker.thread.start();//调run方法
}
//拒绝策略 方法
private void reject(Runnable command) {
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler();
rejectedExecutionHandler.rejectedExecution(command);
}
/**
* 工作内容 内部类
*/
class Worker extends ReentrantLock implements Runnable{
private Runnable firstTask;
private Thread thread;
//初始任务
public Worker(Runnable firstTask) {
this.firstTask = firstTask;
thread = new Thread(this);
}
@Override
public void run() {
runWork(this);//调用具体的执行
}
//具体的执行任务方法
private void runWork(Worker w) {
//上锁
try {
w.lock();
Runnable task = w.firstTask;
//判断task 如果不为空,或者去队列取task 不为空
if (task!=null || (task=getTask())!= null){
task.run(); //执行run方法
}
}finally {
processWorkerExit(w); //执行完重复使用
w.unlock();
}
}
//往worker传null 达到重复使用效果
private void processWorkerExit(Worker w){
addWorker(null,false);
}
private Runnable getTask() {
try {
if (workQueue.isEmpty()){
return null;
}
//三目表达式 如果开启了超时 ?传入超时时间获取队列如果是超时了返回null ? 否则直接返回队列元素
Runnable r = allowCoreThreadTimeOut ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS): workQueue.take();
if (r != null){
return r;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
/**
* 拒绝策略 内部类
*/
class RejectedExecutionHandler{
public void rejectedExecution(Runnable command){
throw new RejectedExecutionException("这个task"+command+"被拒绝");
}
}
}
测试方法
public class Test {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,5,new LinkedBlockingDeque<>());
for (int i=0;i<10;i++){
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("MyThreadPoolExecutor");
}
});
}
}
}