常见的线程池类
1、newCachedThreadPool 会根据任务来临的需要决定是否创建新的线程,如果来了新任务又没有空闲线程
,它就会新建一个线程。
ExecutorService m=Executors.newCachedThreadPool();
底层实现new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>())//直接提交队列
m.submit(Runnable run=()->{
System.out.println("线程:"+Thread.currentThread()+"负责了"+count+"次任务");
});
2、newFixedThreadPool 创建一个固定大小的、可重用是线程池(当达到了最大值,线程先存入队列中)。
ExecutorService m=Executors.newFixedThreadPool(4);
底层实现new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>())//无界队列
m.submit(Runnable run=()->{
System.out.println("线程:"+Thread.currentThread()+"负责了"+count+"次任务");
});
3、newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
ScheduledExecutorService m = Executors.newScheduledThreadPool(4);
底层实现new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue())//优先级队列
m.scheduleAtFixedRate(Runnable run=()->{
System.out.println("线程:"+Thread.currentThread()+"负责了"+count+"次任务");
}, 1 , 1 , TimeUnit.SECONDS);
初始延迟 间隔时间 单位
4、newSingleThreadExecutor()
底层实现new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()))
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
----------------------------------------------------------------
threadPoolExecutor.allowCoreThreadTimeOut(true);
线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用
########其实以上返回的最终都是ThreadPoolExecutor对象##############################
ThreadPoolExecutor详细介绍(根据JDK整理java.util.concurrent)
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//线程池中超过corePoolSize数目的空闲线程最大存活时间;可以
//ThreadPoolExecutor().allowCoreThreadTimeOut(true)成为核心线程的有效时间
TimeUnit unit,//keepAliveTime的时间单位
BlockingQueue<Runnable> workQueue,//阻塞任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler
//当提交任务数超过maxmumPoolSize+workQueue之和时
//任务会交给RejectedExecutionHandler来处理
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
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;
}
构造函数的参数含义如下:
corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,
还是放到workQueue任务队列中去;核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,
默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或
者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建
corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程
去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,
决定线程池会开辟的最大线程数量;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,
keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一
个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。
但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,
keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;
它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
threadFactory:线程工厂,用于创建线程,一般用默认即可;
handler:拒绝策略,当任务太多来不及处理时,如何拒绝任务;
####workQueue任务队列
它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列:
1、new SynchronousQueue<Runnable>()
直接提交队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,
它没有容量,没执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要
等待对应的插入操作当任务队列为SynchronousQueue,创建的线程数大于maximumPoolSize时,直接执行了
拒绝策略抛出异常。使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行
任务的线程数量小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize设置的最大值,
则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种
情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执
行拒绝策略;
2、new ArrayBlockingQueue<Runnable>(10)
使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量
达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始
化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于
maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有
界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时
,则会以maximumPoolSize为最大线程数上限。
3、new LinkedBlockingQueue<Runnable>()
使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你
corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中
缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则
直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列
中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。
4、new PriorityBlockingQueue<Runnable>()
通过运行的代码我们可以看出PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个
任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,
而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。
###拒绝策略
1、AbortPolicy:该策略会直接抛出异常,阻止系统正常工作(常用线程类默认策略);
2、CallerRunsPolicy:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行
3、DiscardOledestPolicy:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的
马上要被执行的那个任务,并尝试再次提交;
4、DiscardPolicy:该策略会默认丢弃无法处理的任务,不予任何处理。当然使用此策略,
业务场景中需允许任务的丢失;
1、new ThreadPoolExecutor(.....).execute(()->{
//执行线程
});
2、new ThreadPoolExecutor(.....).submit(()->{
//执行线程
});
线程池中的execute方法即开启线程执行池中的任务。还有一个方法submit也可以做到,它的功能是提交指定的任务去执行并且返回Future对象,即执行的结果。