在《阿里巴巴java开发手册》中写到,线程池不允许使用Executors 去创建,而是通过 ThreadPoolExecutor 的方式。
Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
那ThreadPoolExecutor类如何使用。
首先是ThreadPoolExecutor的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造函数参数含义:
corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去。
maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量。
keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁。
unit:keepAliveTime的单位。
workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种。
threadFactory:线程工厂,用于创建线程,一般用默认即可。
handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务
一、workQueue任务队列
一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列
1、直接提交队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,每执行一个插入操作就会阻塞。
public class ThreadTest implements Runnable{
private int num;
ThreadTest(int i){
this.num = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"任务"+num);
}
}
@Test
public void threadTest(){
ExecutorService pool = new ThreadPoolExecutor(1, 4, 2000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
for(int i = 1;i<10;i++){
pool.execute(new ThreadTest(i));
}
}
运行结果:
pool-1-thread-1任务1
pool-1-thread-3任务3
pool-1-thread-2任务2
pool-1-thread-4任务4
java.util.concurrent.RejectedExecutionException: Task com.Alm.blog.service.controller.BlogController$ThreadTest@3d51f06e rejected from java.util.concurrent.ThreadPoolExecutor@7ed7259e[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 4]
可以看到,当任务队列为SynchronousQueue时,没有容量,提交的任务会被马上执行,并且执行完后当前线程阻塞,不会复用,所以当创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。
一般使用SynchronousQueue都会要求为无界(maximumPoolSize=Integer.MAX_VALUE)。
2.有界的任务队列:一般使用ArrayBlockingQueue来实现
ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,会将新的任务加入到等待队列中。这里等待队列的任务数量设置为3,若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。
3.无界的任务队列:一般使用LinkedBlockingQueue来实现
ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
使用无界的任务队列,线程池的任务队列可以无限制添加新任务,而线程池创建的最大线程数就是corePoolSize设置的数量,这种情况下maximumPoolSize设置的值是不会生效的,就算等待队列中有许多未执行的任务,线程池的数量达到了corePoolSize的值后也不会增加新的线程。
4.优先任务队列:一般使用PriorityBlockingQueue来实现
public static class ThreadTest implements Runnable,Comparable<ThreadTest>{
private int num;
ThreadTest(int i){
this.num = i;
}
//顺序
//public int compareTo(ThreadTest o) {
// return this.num-o.num;
//}
// 倒序
public int compareTo(ThreadTest o) {
return this.num>o.num?-1:1;
}
@Override
public void run() {
try{
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"任务"+num);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
for(int i = 1;i<10;i++){
pool.execute(new ThreadTest(i));
}
}
运行结果如下:
pool-1-thread-1任务1
pool-1-thread-1任务9
pool-1-thread-1任务8
pool-1-thread-1任务7
pool-1-thread-1任务6
pool-1-thread-1任务5
pool-1-thread-1任务4
pool-1-thread-1任务3
pool-1-thread-1任务2
因为corePoolSize设置为1,且线程休眠了1秒,则除了1以外其他任务全部进入了等待队列中,然后根据指定的按优先级规则进行了重新排列执行,且线程池的线程数一直为corePoolSize。