线程池的概念

线程池由任务队列和工作线程组成,它可以重用线程来避免线程创建的开销,在任务过多时通过排队避免创建过多线程来减少系统资源消耗和竞争,确保任务有序完成;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. 使用一般处理程序生成 JSON

    在 .NET 3.5 之后,定义在命名空间 System.Runtime.Serialization.Json 中的 DataContractJsonSerializer 可以帮助我们直接将一个对象格 ...

  2. react中,用key值来解决一些奇葩问题

    编辑用户信息,角色信息无法加载到值 改进之后:思路:由于值是设置在state里面的,界面编辑时,会重服务器拉去数据,值也设置在state里面了,但是CheckboxGroup依然不会去渲染选中的值, ...

  3. Python3之类和实例访问限制

    在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑. 但是,从前面的Student类定义来看,外部代码还是可以自由地修改一个实例的na ...

  4. iOS-UITabbar自定义

    [self createCustomTabBar]; -(void)createCustomTabBar{    //创建一个UIImageView,作为底图    UIImageView *bgVi ...

  5. linux下的进程通信之信号量semaphore

    概念: IPC 信号量和内核信号量非常相似,是内核信号量的用户态版本. 优点:每个IPC信号量可以保护一个或者多个信号量值的集合,而不像内核信号量一样只有一个值,这意味着同一个IPC资源可以保护多个独 ...

  6. tp5博客项目实战1

    tp5博客项目实战 开发准备:环境wamp,windows系统为例.看实战博客,默认会搭建开发环境并且tp5框架已经至少有一定的基础. tp5的下载与安装 方法一:直接在官网下载拷贝到wamp你的项目 ...

  7. vscode常用插件小结

    工欲善其事,必先利其器. 个人用过的代码编辑器有sublime,webstrom,vscode,H5builder.综合比较下来还是更倾向于vscode. sublime是一款轻量级的编辑器,优点是启 ...

  8. js删除array数组中元素

    for(var i = 0; i < array.length; i++) { if(-1 != array[i]) { array.splice(i, 1); i--; } } 上面if就是判 ...

  9. uniapp跨域两次请求解决方案

    引入qs模块 使用 qs模块将data序列化,再传递,注意header必须设置为 'content-type':'application/x-www-form-urlencoded', import ...

  10. SpringBoot使用thymeleaf的方式引用static中的静态资源

    当我们在开发网站时为了快速完成,避免不了使用第三方的框架文件.这样我们就得引用框架中的各种资源文件.那么,在springboot中通过 thymeleaf如何在html中使用static文件夹下的静态 ...