public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
构造函数中的几个变量意义:
1 corePoolSize >= 0 线程池中核心线程数量, 这些线程时不会回收的
2 maximumPoolSize >= corePoolSize 线程池中最大线程数量,非核心线程 在执行任务之后,如果到达空闲时间则销毁
3 keepAliveTime 非核心线程执行结束之后,多长时间后将其销毁,如果为0,则永远不会销毁,
4 workQueue >0: 由于存储新的任务
5 RejectedExecutionHandler: 任务队列已满,且达到最大核心线程数时的 拒绝策略:abortPolicy(抛出异常),
DiscardPolicy(直接丢弃当前任务),DiscardOldesPolicy(丢弃最老的任务)
线程池中执行线程的顺序:
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 (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
从代码可以看到:
1 如果当前工作线程数量小于核心线程数,创建新线程 执行任务
2 如果当前工作线程数大于等于核心线程,则将新的任务存储到等待队列中
3 如果等待队列已满,且为达到最大线程数,则从任务队列中取出任务并创建非核心线程执行任务
4 如果任务队列已满,且达到最大线程数,则执行拒绝策略
java 中提供了一些创建线程池的Api
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
上面两个时创建固定线程数量的线程池以及单一线程的线程池,它们的特点的corePoolSize == maxPollSize,且keepAliveTime = 0,即线程空闲后,也不会销毁
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
创建Cached线程池,corePoolSize 为0 ,maxPoolSize为Integer.MAX_VALUE,线程空闲后的存活时间为1个小时
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
10L, MILLISECONDS,
new DelayedWorkQueue());
}
创建定时发送命令的线程池
1. 缓冲队列BlockingQueue简介:
BlockingQueue是双缓冲队列。BlockingQueue内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。
2. 常用的几种BlockingQueue:
-
ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
-
LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
-
一把锁,两个condition,notFull(存操作时,如果满了,需要block,等待notfull信号释放) ,notEmpty(取操作时,如果队列无数组,则block,等待notEmpty操作释放)
-
PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
-
SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成
3. 自定义线程池(ThreadPoolExecutor和BlockingQueue连用):
自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池。
常见的构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
https://blog.csdn.net/sky_100/article/details/75174250
如何选择核心线程数 可参考下面的文章
https://blog.csdn.net/tbdp6411/article/details/78443732
一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数),IO密集型应用线程对CPU占用较少,通常设置的核心线程数多一点,计算密集型应用 线程对CPU占用时间很多,为了担心CPU 被占满,不适宜设置太多的线程数。
- 如果是CPU密集型应用,则线程池大小设置为N+1
- 如果是IO密集型应用,则线程池大小设置为2N+1