1.线程池出现的原因:
在多线程的情况下,多个线程在创建和销毁的阶段会消费很多时间,占有很多资源,线程池为了减少在创建和销毁的资源下出现:
2.JAVA中的ThreadPoolExecutor线程池
2.1 线程池的运行方式
当线程池中获取到一个需要执行的线程的时候,是如何运行的呢?
此处可以通过对源码的查看了解:
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))//执行addWorker,会创建一个核心线程,如果创建失败,重新获取ctl
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { //表示线程池为running状态,切队列可以添加任务
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //线程池不是running状态 则将线程从队列中移除 并执行对应策略
reject(command);
else if (workerCountOf(recheck) == 0)//如果移除失败,则会判断工作线程是否为0 ,如果过为0 就创建一个非核心线程
addWorker(null, false);
}
else if (!addWorker(command, false))//若队列已满 并且不能再线程池中创建线程
reject(command);
}
其中 private boolean addWorker(Runnable firstTask, boolean core)中的参数分别为任务,和对应是否为核心线程
具体方案如下:
获取到一个执行的任务,首先判断当前运行的线程数是否小于核心线程数,表示核心线程是否有空闲,若有则创建核心线程或是在核心线程中执行。若添加任务成功则直接退出该方法,否则继续。
当核心线程都已经被占用,判断等待队列是否满了,如果没满,则将其加入等待队列,如果满了 则判断是否超过线程池的最大容量线程数,若超过则直接进行对应的策略,若没有则创建线程执行任务。
附加:workQueue的offer表示往队列(队列部分可先往下查看在返回看具体实现)中添加,若能往队列中添加则返回true,否则返回false
具体的实现是通过addWorker。
isRunning(c)表示线程池的状态是RUNNING
2.2 线程池的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
上述为ThreadPoolExecutor的核心构造函数,其他构造函数都是调用该构造函数执行。
参数解释:
1.corePoolSize 表示线程池中的核心线程个数,什么是核心线程?表示在线程池中创建完以后就不会销毁的线程。
2.maximumPoolSize 表示线程池中最大容量的线程数
3.keepAliveTime 线程池中除了核心线程,其他线程都是有存活时间的,表示其他线程的存活时间(表示其他线程的最大空闲的时间)
4.unit: 计算时间的单位
5.workQueue: 等待队列,用于存储等待执行的线程,FIFO原则。
6.threadFactory 表示创建线程的工厂
7.handler 拒绝策略,在线程等待队列满了之后,且线程池的中的线程个数大于等于maximumPoolSize,执行某些策略。
workQueue
常用的等待队列有哪些?
ArrayBlockingQueue:表示一个有界队列,其构造函数必须指定队列的大小(采用的是加锁的机制 实现对队列中添加元素或删除元素)
如:public ArrayBlockingQueue(int capacity),public ArrayBlockingQueue(int capacity, boolean fair),ArrayBlockingQueue(int capacity, boolean fair,Collection<? extends E> c)
LinkedBlockingQueue:直接调用 new LinkedBlockingQueue<Runnable>()情况下,是为一个无界队列,但亦可以指定队列的大小。与ArrayBlockingQueue其他的不同就相当于ArrayList和LinkedList的区别。(采用的是加锁的机制 实现对队列中添加元素或删除元素)
在jdk自带的Executors工厂构建一个newSingleThreadExecutor等方法 都是调用了new LinkedBlockingQueue<Runnable>()
具体参考https://mp.csdn.net/editor/html/111346385
threadFactory
通过对源码发现,只需是实现创建线程的方法,在创建线程池默认情况下,如果没有传入参数threadFactory的时候,会调用Executors工厂下的默认线程池方法
将线程的优先级都设置为普通优先级
handler
线程池中提供了4种策略:
1.CallerRunsPolicy 直接调用r.run();的方法(直接执行任务 不启用新线程)执行任务
2.AbortPolicy 不执行新任务,并抛出异常
3.DiscardPolice:直接抛弃该任务,不抛弃任何异常
4.DiscardOldestPolicy 将线程池队列中最老的等待任务抛弃,然后执行e.execute(r);(线程池执行任务的方法)
5.若想要通过自己的设置策略实现则通过实现接口 RejectedExecutionHandler即可。