线程池的概念

线程池由任务队列和工作线程组成,它可以重用线程来避免线程创建的开销,在任务过多时通过排队避免创建过多线程来减少系统资源消耗和竞争,确保任务有序完成;ThreadPoolExecutor 继承自 AbstractExecutorService 实现了 ExecutorService 接口,ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor 实现了 ExecutorService 和 ScheduledExecutorService 接口

//有多个构造方法,最终都指向这个最多参数的构造方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }

corePoolSize:核心运行的线程个数,也就是当超过这个范围的时候就需要将新的异步任务放入到等待队列中,小于这个数时添加进来的异步任务一般直接新建Thread 执行;

maximumPoolSize:最大线程个数,当大于了这个值就会将准备新加的异步任务由一个丢弃处理机制来处理,大于 corePoolSize 且小于 maximumPoolSize 则新建 Thread 执行,但是当通过newFixedThreadPool 创建的时候,corePoolSize 和 maximumPoolSize 是一样的,而corePoolSize 是先执行的,所以他会先被放入等待队列而不会执行到下面的丢弃处理中;

workQueue:任务等待队列,当达到 corePoolSize的时候就向该等待队列放入线程信息(默认为一个LinkedBlockingQueue);

keepAliveTime:默认是 0,当线程没有任务处理后空闲线程保持多长时间,不推荐使用;

threadFactory:是构造 Thread 的方法,一个接口类,可以使用默认的 default实现,也可以自己去包装和传递,主要实现 newThread 方法即可;

defaultHandler:当参数 maximumPoolSize 达到后丢弃处理的方法实现,java 提供了 5种丢弃处理的方法,当然也可以自己弄,主要是要实现接口 RejectedExecutionHandler 中rejectedExecution(Runnabler, ThreadPoolExecutor e) 方法,java 默认使用的是AbortPolicy,他的作用是当出现这种情况的时候抛出一个异常;通常得到线程池后会调用其中的 submit 或 execute 方法去提交执行异步任务,其实 submit 方法最终会调用execute 方法来进行操作,只是他提供了一个 Future

来托管返回值的处理而已,当你调用需要有返回值的信息时用它来处理是比较好的,这个 Future 会包装 Callable 信息。

BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类

  1. ArrayBlockingQueue:一个由数组支持的有界阻塞队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。
  2. LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。
  3. PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。
  4. SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。

说了这么多的概念估计啥也不清楚,带大家写个例子,一边看代码一边看概念会理解的很快

public class ThreadPoolTest { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = Math.max(4, Math.min(CPU_COUNT - 1, 5)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 2; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(10); //一共执行20个任务 ,核心线程数是4,最大核心线程数是10,目前加入的runnable20个(相当于20个任务), //20个任务需要执行,但是核心线程数只有4个,还有16个任务,由于LinkedBlockingQueue队列是最大存放的任务为10 个,队列满了,则会创建新的线程去执行任务,这个时候最大线程是10, 非核心线LinkedBlockingQueue数还有6个,这时候会开6个线程去执行, 目前达到10个最大线程数,此时队列里面还有10个。正好满足队列的大小 static { System.out.println("核心线程数=" + CORE_POOL_SIZE); System.out.println("最大线程数=" + MAXIMUM_POOL_SIZE); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, //核心线程数 MAXIMUM_POOL_SIZE, //线程池中最大的线程数 60, //线程的存活时间,没事干的时候,空闲的时间 TimeUnit.SECONDS, //线程存活时间的单位 sPoolWorkQueue, //线程缓存队列 new ThreadFactory() { //线程创建工厂,如果线程池需要创建线程会调用newThread来创建 @Override public Thread newThread(@NonNull Runnable r) { Thread thread = new Thread(r); thread.setDaemon(false); return thread; } }); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; } public static void main(String[] args) { for (int i = 0; i < 20; i++) { Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("执行完毕" + Thread.currentThread().getName()); } }; //丢给线程池去执行 THREAD_POOL_EXECUTOR.execute(runnable); } } }

核心的解释,大家请看注释

运行效果

看这个例子

public class ThreadPoolTest { private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = Math.max(4, Math.min(CPU_COUNT - 1, 5)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 2; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(9); //一共执行20个任务 ,核心线程数是4,最大核心线程数是10,目前加入的runnable20个(相当于20个任务), //20个任务需要执行,但是核心线程数只有4个,还有16个任务,由于LinkedBlockingQueue队列是最大存放的任务为9个,队列满了,则会创建新的线程去执行任务 //这个时候最大线程是10,非核心线程数还有6个,这时候会开6个线程去执行,目前达到10个最大线程数,此时队列里面最大只能存放9个, //还有一个Runnable,此时就会报错RejectedExecutionException static { System.out.println("核心线程数=" + CORE_POOL_SIZE); System.out.println("最大线程数=" + MAXIMUM_POOL_SIZE); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, //核心线程数 MAXIMUM_POOL_SIZE, //线程池中最大的线程数 60, //线程的存活时间,没事干的时候,空闲的时间 TimeUnit.SECONDS, //线程存活时间的单位 sPoolWorkQueue, //线程缓存队列 new ThreadFactory() { //线程创建工厂,如果线程池需要创建线程会调用newThread来创建 @Override public Thread newThread(@NonNull Runnable r) { Thread thread = new Thread(r); thread.setDaemon(false); return thread; } }); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; } public static void main(String[] args) { for (int i = 0; i < 20; i++) { Runnable runnable = new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("执行完毕" + Thread.currentThread().getName()); } }; //丢给线程池去执行 THREAD_POOL_EXECUTOR.execute(runnable); } } }

这个例子就是把LinkedBlockingQueue的大小改为了9个,具体的解释,请大家看注释;大家可以清楚的知道RejectedExecutionException 报错的原因,其实是AsyncTask一些隐患,比如去执行200个Runnable 肯定会报错

运行效果

相信大家看例子的同时在结合概念会很清楚的理解了java线程池

写在最后,欢迎留言讨论,持续更新!!!

干货,阿里P8浅谈对java线程池的理解(面试必备)的更多相关文章

  1. 干货:教你如何监控 Java 线程池运行状态

    之前写过一篇 Java 线程池的使用介绍文章<线程池全面解析>,全面介绍了什么是线程池.线程池核心类.线程池工作流程.线程池分类.拒绝策略.及如何提交与关闭线程池等. 但在实际开发过程中, ...

  2. java线程池ThreadPoolExecutor理解

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  3. java线程池初步理解

    多线程基础准备 进程:程序的执行过程,持有资源和线程 线程:是系统中最小的执行单元,同一个进程可以有多个线程,线程共享进程资源 线程交互(同步synchronized):包括互斥和协作,互斥通过对象锁 ...

  4. 浅谈对java中锁的理解

    在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性.synchronized机制是给共享 ...

  5. Java线程池详解(二)

    一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...

  6. Java线程池的底层实现与使用

    前言 在我们进行开发的时候,为了充分利用系统资源,我们通常会进行多线程开发,实现起来非常简单,需要使用线程的时候就去创建一个线程(继承Thread类.实现Runnable接口.使用Callable和F ...

  7. 20190608_浅谈go&java差异(三)

    20190608_浅谈go&java差异(三) 转载请注明出处https://www.cnblogs.com/funnyzpc/p/10990703.html 第三节内容概览 多线程通讯(线程 ...

  8. 干货 | 教你如何监控 Java 线程池运行状态

    之前写过一篇 Java 线程池的使用介绍文章<线程池全面解析>,全面介绍了什么是线程池.线程池核心类.线程池工作流程.线程池分类.拒绝策略.及如何提交与关闭线程池等. 但在实际开发过程中, ...

  9. Java线程池进阶

    线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...

随机推荐

  1. NET Core 3.0中的WPF

    在.NET Core 3.0中的WPF中使用IOC图文教程   我们都知道.NET Core 3.0已经发布了第六个预览版,我们也知道.NET Core 3.0现在已经支持创建WPF项目了,刚好今天在 ...

  2. linux中查看磁盘容量的常用操作

    linux中查看磁盘容量常用操作 实验室有GPU集群,用户跑数据时候跑着跑着会出现集群挂掉的问题,原因就是,在跑数据时,用户上传文件,数据集,系统产生缓存等一系列操作,消耗了集群空间,师兄让我清理下服 ...

  3. ASP.NET请求过程-Module

    管道模型     上图中为Http请求在Asp.net程序中处理的过程.管道处理模型来自上面的HttpApplication,管道处理模型其实就是多个Module(其实这些module都是在往http ...

  4. 认识 Spring 框架(一)

    认识 Spring 框架 Spring 框架是 Java 应用最广的框架,它的成功来源于理念,而不是技术本身,它的理念包括 IoC (Inversion of Control,控制反转) 和 AOP( ...

  5. 数位dp踩坑

    前言 数位DP是什么?以前总觉得这个概念很高大上,最近闲的没事,学了一下发现确实挺神奇的. 从一道简单题说起 hdu 2089 "不要62" 一个数字,如果包含'4'或者'62', ...

  6. 理解 is_callable

    官方解释: (PHP 4 >= 4.0.6, PHP 5, PHP 7) is_callable — 检测参数是否为合法的可调用结构. 说明 is_callable ( callable $na ...

  7. 1,electron搭建

    electron可以用js来创建丰富的桌面应用 一个electron应用的目录结构 your-app/ ├── package.json ├── main.js └── index.html 1,安装 ...

  8. centos7搭建EFK日志分析系统

    前言 EFK可能都不熟悉,实际上EFK是大名鼎鼎的日志系统ELK的一个变种 在没有分布式日志的时候,每次出问题了需要查询日志的时候,需要登录到Linux服务器,使用命令cat -n xxxx|grep ...

  9. nodejs 入门学习

    Node.js学习笔记——Node.js开发Web后台服务   目录 一.简介 二.搭建Node.js开发环境 2.1.安装Node.js 2.2.安装IDE开发Node.js插件 三.第一个Node ...

  10. BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)

    二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...