Java中的线程池做过Java开发的人肯定都知道了,什么如何创建线程池啊,一些创建时候的参数这里就不多说了,这篇主要是想写一下自己在以前使用线程池的过程中一些误解和不清晰的地方,写下来分享一下,也给自己留个印象.
先说一下之前的错误理解吧,线程池的创建应该不用多说,都是推荐使用new ThreadPoolExecutor来创建,包括Springboot中包装了之后的ThreadPoolTaskExecutor,内部本质也是一个ThreadPoolExecutor,然后在new一个ThreadPoolExecutor的时候最后都是调用
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
这个构造方法,共有7个参数,一个个来讲吧,第一个参数是核心线程数量.第二个是最大线程数量,第三个和第四个参数是线程空闲的多久后回收释放,第五个参数是线程池的任务队列,第六个参数是线程池的线程创建工厂类,第七个参数是线程池的拒绝策略.
在刚开始用到线程池的时候,我以为核心线程自然不用说了,肯定是线程池创建的时候就出来了,然后一直到线程池关闭线程才结束,最大线程数也应该是核心线程干活干不完了,那就增加线城数来干活,一直加到最大线程数的限制,超时时间肯定是只作用于非核心线程.
后来在实际使用的过程中已经自己对线程池的源码阅读,发现全部都理解错了,下面就一条条的通过源码来解读真实情况是什么样的.
错误理解1:核心线程肯定是线程池已创建的时候就创建好了
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize
maximumPoolSize <= 0 ||
maximumPoolSize
keepAliveTime
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;
}
上面的代码就是线程池创建时候的代码,里面除了初始化一些配置参数外,没有任何线程创建的影子,那核心线程到底是什么时候创建的呢,实际上不管是核心线程还是非核心线程,都是在你提交任务的时候创建的,也就是你execute(Runable r)的时候,
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if ((c)
if (addWorker(command, true))
return;
c = ctl.get();
}
if ((c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! (recheck) && remove(command))
reject(command);
else if ((recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
单纯的看代码估计有点晦涩难懂,但是看一下代码上面的注释,一下就清晰了很多了,简单翻译一下就是1.如果当前线程池中的线程数少于corePoolSize,那么就创建线程并执行提交的任务,2.如果任务可以成功的添加到任务队列中,那就添加到任务队列中(其中还有二次检查是否需要添加线程的代码)3.添加一个非核心线程并执行提交的任务,添加失败就reject这个任务
通过以上注释和分析,可以很明显看出哪里理解错了,就是线程池的核心线程只有在提交任务的时候才会被创建,并且必须提交一个任务才会创建一个核心线程.
错误理解2:核心线程肯定是一直到线程池关闭的时候才释放,超时时间的设置不会影响核心线程.
其实ThreadPoolExecutor有一个public void allowCoreThreadTimeOut(boolean value) ,是public 的方法,也就是说可以通过调用该方法设置是否超时后释放核心线程,具体代码就不贴了,有兴趣的可以自己看一下.
错误理解3:非核心线程会在核心线程都在忙的时候创建.然后等待空闲了超过超时时间之后被回收并释放资源.
其实这个看一下刚刚那个代码就会知道肯定是错的了,增加非核心线程是在最后一步才执行,也就是上面注释里说的3,总结一下就是,必须等到核心线程都在忙,并且任务队列已经被塞满的情况下,才会去创建非核心线程,而不是我之前理解的只要核心线程都在忙的时候就回创建.
以上就是我这次的理解,感觉还是要多看源代码和注释,不应该凭借想象力去猜测他的工作方式,不然只会自以为是的一直抱着错误的理解.
转载时请注明出处及相应链接,本文永久地址:https://blog.yayuanzi.com/25259.html
微信打赏
支付宝打赏
感谢您对作者Miya的打赏,我们会更加努力! 如果您想成为作者,请点我