概述
创建线程池的四种方式
newSingleThreadExecutor
创建一个拥有一个线程的线程池,该线程池的优点是保证事件的执行顺序(先进先出队列(FIFO)、后进先出(LIFO))
newFixedThreadPool(int nThreads)
创建一个定长线程池,传进去的参数为可同时并发的线程数,超出的任务放进阻塞队列
newScheduledThreadPool(int corePoolSize)
创建一个定长的能执行周期任务的线程池
newCachedThreadPool()
创建可以缓存的线程池,若是事件数小于线程池内线程数,则jvm可回收,反之再创建线程
我们先来看看创建线程池内的构造参数
corePoolSize:线程池的基本大小(核心线程数)
maximumPoolSize:最大线程数(非核心线程数)
keepAliveTime:线程保持时长,设置线程过期时间
TimeUnit unit:时长单位(TimeUnit.MILLISECONDS、TimeUnit.SECONDS)
workQueue:工作队列
defaultHandler:拒绝策略
下面我们来给大家展开讲每一种线程池的优缺点
newSingleThreadExecutor()
我们先来看看newSingleThreadExecutor()的源码
从上面的源码我们可以看出,核心线程数为1、最大线程数也为1,所以执行任务时只是一个线程去执行,剩下的加入工作队列,比如我过来了50个任务,只能一个个的去执行,能保证任务的执行顺序,但是执行效率没有其它的线程池高,我们来看看该线程池的结构图
newFixedThreadPool(int nThreads)
传进去的int nThreads,是核心线程数,也是最大线程数,线程池结构图
我们来着重看看这个LinkedBlockingQueue工作队列,先看看它的源码
它是一个无界队列,也就是说无论多少任务我都照单全收,但是这非常的危险,因为可能会由于线程阻塞等原因,任务执行的慢,可能会造成OOM
newCachedThreadPool()
先来看看源码和结构图,我们从源码结合结构图来分析
看着源码和结构图我们能明白,核心线程数为0,来的所有任务会直接塞进阻塞队列(SynchronousQueue)中,什么是阻塞队列呢?看到上面的结构图,阻塞队列只有一个块,也就是说有一个任务到阻塞队列中,线程池得立马起一个线程去响应它,要不然它会一直阻塞在队列中,别的任务进不来
newScheduledThreadPool(int corePoolSize)
调用newScheduledThreadPool(int corePoolSize)的方法时也就会直接调用父类创造线程池的方法,也就是我们下面的这个基本的方法,为什么它可以定时执行呢?最主要还是在它的队列上,new DelayedWorkQueue()
new DelayedWorkQueue()
是一个延时队列,可以根据设置的延时时间取到队列中任务,然后启动线程执行,实现定时执行的效果
现在我们回归正题,为什么java给了我们这么方便使用线程池的方式,阿里还不推荐使用呢?
我们回过头来看看,在newSingleThreadExecutor
、newFixedThreadPool(int nThreads)
使用到的队列,它是一个无界队列啊,我如果有10000W个任务呢???我的天,JVM都快哭了吧,所以说当线程数过多时,没有做到一个很好的限制,全部都往队列里加容易造成OOM,这在正式环境中是一个致命的问题
那newScheduledThreadPool(int corePoolSize)
、newCachedThreadPool()
这两个呢?我们来看看它的maximumPoolSize,都为Integer.MAX_VALUE,这样会有一个问题,我还是10000W个任务,我就会起10000W个线程来执行,这都能把电脑的CPU干着火了都
一个是对内存的消耗,还有一个是让CPU的负载过重,所以阿里不推荐这四种创建线程池的方式
阿里支持的是自定义线程的创建,我上代码给大家看看怎么自定义线程
所以要根据实际任务来自定义线程池,就不会出现上面那四种常规方法创建线程池所出现的问题,但是!!
有一个小问题,我用一幅图给大家描述一下,最坏的情况
还有十个任务呢?它们去哪了?我直接说了吧,它会直接报错,这就要涉及到一个知识点了,拒绝策略(ThreadFactory)
举个栗子
public class thread {
public static void main(String[] args) {
ExecutorService self = new ThreadPoolExecutor(
10,
20,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(20));
for(int i = 50;i<100;i++){
self.execute(new MyThread());
}
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(MyThread.currentThread());
}
}
执行结果:
会报错,这就是拒绝策略的作用,当我们自定义的线程池接收不了这个任务时,会直接抛出异常,关于里面的源码我就不细讲了,感兴趣的同学可以在下面评论催催更,说不定明天我就更新了呢
谢谢你们的阅读,给个赞赞赞赞赞来鼓励我吧
完成:TO: 2021/3/30 16:47
最后
以上就是从容白云为你收集整理的阿里规约---为什么建议使用ThreadPoolExecutor自定义线程池创建线程池的四种方式newSingleThreadExecutor()newFixedThreadPool(int nThreads)newCachedThreadPool()newScheduledThreadPool(int corePoolSize)的全部内容,希望文章能够帮你解决阿里规约---为什么建议使用ThreadPoolExecutor自定义线程池创建线程池的四种方式newSingleThreadExecutor()newFixedThreadPool(int nThreads)newCachedThreadPool()newScheduledThreadPool(int corePoolSize)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复