多线程之线程池(上)

云中志

共 2842字,需浏览 6分钟

 · 2021-07-17


前言

最近一段时间,我们一直都在分享多线程相关知识,也一直有用线程池,但是一直没有介绍线程池相关知识,所以今天我们就先来看下线程池相关的知识点。

线程池

ThreadPool,线程池,顾名思义就是存放线程的池子,也是jdk1.5引入的。对我们而言,它的最主要优势就是简化了线程启动流程,让我们可以更方便地使用多线程,再也不用手动start线程,直接通过线程池提交我们的任务即可,而且合理使用线程池至于可以带来以下几个好处:

  • 降低资源消耗:复用线程,降低创建和销毁线程带来的资源消耗

  • 提高响应速度:使用线程池,省去了线程创建和初始化过程,所以任务可以更快执行

  • 提高线程的可管理性:可以直接通过线程池管理、监控、调度线程,线程管理更方便

常用线程池

常用的线程池有SingleThreadExecutorCachedThreadPoolScheduledThreadPoolFixedThreadPool,他们分别是单线程调度器,缓存线程池,定时任务线程池和固定线程池,他们都可以通过Executors创建,调用对应的静态方法即可,由于这一块的内容比较多,所以今天就简单提一下,后面专门讲一次。

自定义线程池

我们今天着重讲下自定义线程池,自定义线程池也很简单,直接new ThreadPoolExecutor(),然后传入对应的参数即可,大家可以看下下面的示例:

int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 1000;
TimeUnit unit = TimeUnit.MICROSECONDS;
BlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
for (int i = 0; i < 50; i++) {
    threadPoolExecutor.execute(() -> {
        String name = Thread.currentThread().getName();
        System.out.println("hello threadPool: "+ name);
    });
}
threadPoolExecutor.shutdown();

ThreadPoolExecutor有三个构造方法,至少需要三个参数

其中第一个参数是线程池的基本大小,当你提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使有空闲的线程存在,线程池依然会启动一个新线程来执行当前任务,直到线程池中的线程数达到线程基本大小(corePoolSize);

第二个参数是线程池允许创建的最大线程数。如果工作队列(第三个参数)满了,且创建线程数已达到线程池基本大小,则线程池会继续创建新的线程来执行任务。如果你指定的工作队列是无界的,那这个参数也就失效了。

第三个参数就是线程池工作队列,就是当你需要执行的任务超过线程池基本大小的时候,会把超出部分放进工作队列,等待线程池基本线程资源释放。

下面我们分别验证以上三点,运行上面的示例代码:

在第一次循环的时候(i=0),我们发现线程池的size0,活动线程数也是0,任务队列也是0,完成任务数也是0,这也说明线程池在最开始的时候是没有创建线程的;

然后我们让他循环到第9次(i=8),这时候线程池已经被初始化,有8个线程(由于断点的原因,第9个线程尚未被创建),活动线程数5,完成执行的任务数3,任务队列还是0,说明确实在未达到线程池基本大小时,会不断创建新的线程;

我们继续执行,让他循环到第15次(i=14),可结果似乎和我们预期不一样,按照预期,线程池的size应该是10,活动线程数也是10,任务队列也是4,完成任务数可能不确定,所以这里肯定不能通过debug的方式来看了,因为debug停顿之后好多线程资源已经被释放,任务根本就不会堆积,所以任务队列就不会有数据:

所以这里我在线程池启动任务前加一行打印,打印线程池数据

然后在运行,就可以拿到运行数据:

这样的数据才是真实的,因为实际运行的时候,线程启动是非常快的,所以执行完成的任务数应该是0,等待任务数是4

我们前面设定的最大线程数是20,但是翻看运行记录,我发现线程池的大小始终是10,说明只要不打到任务队列的上限,并不会创建新的线程,这里我们把循环次数改为60,然后运行下:

但是依然没有创建新的线程,因为还是没有达到任务队列上限,我们把循环次数再调大一点,调到70:

现在线程池的大小就变成了19,活动线程数19,但是这时候如果你继续调大循环次数,线程池就会报错了:

这个错误的原因就是线程池资源已经耗尽了,无法再接收新的任务了,这也就是说线程池能够处理的最大任务数是corePoolSize + maximumPoolSize + workQueue.size()  ,当然,如果你的workQueue不设定大小,那永远都不会报这个错误,当然maximumPoolSize也就无效了。

总结

原本打算线程池一次分享完的,但是实际分享过程中发现内容太多了(已经一千五百字了),所以今天就先到这里,明天再继续分享线程池其他内容。总的来说,今天的内容已经说明白了线程池很多基础的知识点(反正我自己觉得我都有好多收获),算是干货满满吧,你如果掌握了这些知识点,至少在使用线程池的过程中会少踩好多坑。好了,今天就先说这么多吧!

- END -


浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报