让人思来想去的ThreadPool

简介: 一直以来被一个线程池中核心线程跟非核心线程有什么区别的问题困惑着自己,是时候直面恐惧了。

线程池的意义

避免线程过多创建产生的资源浪费,及性能瓶颈,控制资源总量,复用线程,加快响应速度,合理利用cpu和内存,便于管理,以及代码的维护更加整洁

适合场景

服务器端开发,tomcat就用到了线程池。

超过5个线程协作的方法,需要使用线程池。

注意事项

队列积压过多任务,导致内存溢出

线程数量太多导致内存溢出


线程池的参数

线程池的状态

running:接受新任务并处理排队任务

shutdown:不接受任务但是处理排队任务

stop:不接受新任务,也不处理排队任务,并中断正在进行的任务

tidying:所有任务都已终止,workerCount为0,并将运行terminate()钩子方法

terminate:terminate()方法运行完成

线程池核心参数

corePoolSize:核心线程数,当没有满足核心线程数时创建线程执行任务

maximumPoolSize:最大线程数,当任务队列满了,且线程数不满足最大线程数时创建线程执行任务

keepAliveTime:无任务时,线程存活时间,当达到该时间且没有任务时,该线程会被标记成可回收。

unit:时间单位

workQueue:任务队列,存放任务的

threadFactory:线程工厂,通过怎样的工厂类创建线程

handler:当不能工作时的拒绝策略,当达到最大线程数及队列满了的时候,触发拒绝机制,以及线程池关闭的时候

线程数

有关线程池中,线程数的设置;

计算密集型,corePoolSize = cpu 个数 + 1  = maximumPoolSize

IO密集型,则因为线程并不总是在工作,基于线程工作时间与等待时间求比+1 * cpu 的占用比例* cpu 的数量,随着等待时间与工作时间的比例,在一定区间内进行浮动。

如果是混合型的计算需求,那么就具体按照机器的性能,进行设置线程数。

源码中线程数量与线程池的状态用了一个AtomicInteger 进行存储,线程池的状态有5种,使用高3位进行表示,最大线程数其实不能超过61位能标识的最大数。

核心线程与非核心线程之区别

代码分析

开始创建时有些许差异,我们来看java.util.concurrent.ThreadPoolExecutor 的execute 方法:

publicvoidexecute(Runnablecommand) {
if (command==null)
thrownewNullPointerException();
intc=ctl.get();
if (workerCountOf(c) <corePoolSize) {
// 创建核心线程if (addWorker(command, true))
return;
c=ctl.get();
    }
if (isRunning(c) &&workQueue.offer(command)) {
intrecheck=ctl.get();
if (!isRunning(recheck) &&remove(command))
reject(command);
elseif (workerCountOf(recheck) ==0)
// 线程数扩容addWorker(null, false);
    }
elseif (!addWorker(command, false))
reject(command);
}

其实可以看到线程池初始化线程时addWorker 方法是传了firstTask 的,扩容线程时则传的是null,这可以是一个差异点,不过当线程池都创建好后,不再有差异,只会存活核心线程数量那么多的线程,达到超时时间的线程会被设置成可销毁状态,有代码为证:  

finalvoidrunWorker(Workerw) {
Threadwt=Thread.currentThread();
Runnabletask=w.firstTask;
w.firstTask=null;
w.unlock(); // allow interruptsbooleancompletedAbruptly=true;
try {
while (task!=null|| (task=getTask()) !=null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;// if not, ensure thread is not interrupted.  This// requires a recheck in second case to deal with// shutdownNow race while clearing interruptif ((runStateAtLeast(ctl.get(), STOP) ||                 (Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
task.run();
afterExecute(task, null);
                } catch (Throwableex) {
afterExecute(task, ex);
throwex;
                }
            } finally {
task=null;
w.completedTasks++;
w.unlock();
            }
        }
completedAbruptly=false;
    } finally {
processWorkerExit(w, completedAbruptly);
    }
}

我们可以从上面代码看到的是,当worker 在执行任务的时候,firstTask 被设置成了null,其实跟之后扩容的线程也就没太大区别了;

总结

总结下为什么扩容的线程不包含first task,其实是想让新建的线程在任务队列中获取任务进行执行;

那为什么有的线程初次创建时却携带first task 是因为线程池在创建时并没有创建线程,而是等任务来了以后才创建的线程,不得不佩服JDK 的创造者们啊。


总结的有些仓促,有机会重新整理下内容,大家加油!


相关文章
|
5月前
|
Java 调度
Java多线程:什么是线程池(ThreadPool)?
Java多线程:什么是线程池(ThreadPool)?
49 0
|
7月前
|
Java
Thread、Runnable、线程池
Thread是Java中的一个类,用于表示一个线程,它实现了Runnable接口。 通过创建Thread对象,可以创建并启动一个新的线程,执行指定的代码。
ThreadPoolExecutor的中的submit和FutureTask || 通过Executors 创建线程池的一些实例(Callable和Runnable的在其中的体现)
ThreadPoolExecutor的中的submit和FutureTask || 通过Executors 创建线程池的一些实例(Callable和Runnable的在其中的体现)
123 0
|
数据采集 缓存 安全
Executors 与 Thread 比较
Executors 与 Thread 比较
75 0
|
消息中间件 Dubbo Java
“既生 ExecutorService, 何生 CompletionService?”
“既生 ExecutorService, 何生 CompletionService?”
“既生 ExecutorService, 何生 CompletionService?”
|
存储 安全
使用ExecutorService来停止线程服务
使用ExecutorService来停止线程服务
|
存储 缓存 监控
ThreadPoolExecutor:线程池不允许使用Executors创建
ThreadPoolExecutor:线程池不允许使用Executors创建
349 0
ThreadPoolExecutor:线程池不允许使用Executors创建
|
Java .NET 开发框架
ThreadPool类(线程池)
原文:ThreadPool类(线程池) CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。线程池初始化时是没有线程的,线程池里的线程的初始化与其他线程一样,但是在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。
1078 0
|
Java C++ 调度
线程池 ThreadPool
一、线程池 在使用C++的经历中,经常使用多线程(计算密集型),也经常会思考要如何对多线程控制,但没有采用过线程池思想的实现。 在java并发的学习过程中,了解了Java并发组件J.
1293 0
|
Java
ExecutorService
    import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.
900 0