当前位置:首页 > Java技术 > java多线程(四)-自定义线程池

java多线程(四)-自定义线程池

2022年11月08日 20:36:32Java技术10

当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的。

1 public static ExecutorService newCachedThreadPool() {
2         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3                                       60L, TimeUnit.SECONDS,
4                                       new SynchronousQueue<Runnable>());
5 }
1   public static ExecutorService newFixedThreadPool(int nThreads) {
2         return new ThreadPoolExecutor(nThreads, nThreads,
3                                       0L, TimeUnit.MILLISECONDS,
4                                       new LinkedBlockingQueue<Runnable>());
5  }

 

包括其他几种不同类型的线程池,其实都是通过 ThreadPoolExecutor这个核心类来创建的,如果我们要自定义线程池,那么也是通过这个类来实现的。

java多线程(四)-自定义线程池 _ JavaClub全栈架构师技术笔记

 

该类有四个构造方法,查看源码可以看到,头三个构造方法,其实都是调用的第四个构造方法,所以我们就解释一下第四个构造方法的参数含义。

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

 

corePoolSize:核心线程池的大小,在线程池被创建之后,其实里面是没有线程的。(当然,调用prestartAllCoreThreads()或者prestartCoreThread()方法会预创建线程,而不用等着任务的到来)。当有任务进来的时候,才会创建线程。当线程池中的线程数量达到corePoolSize之后,就把任务放到 缓存队列当中。(就是 workQueue)。

maximumPoolSize:最大线程数量是多少。它标志着这个线程池的最大线程数量。如果没有最大数量,当创建的线程数量达到了 某个极限值,到最后内存肯定就爆掉了。
keepAliveTime:当线程没有任务时,最多保持的时间,超过这个时间就被终止了。默认情况下,只有 线程池中线程数量 大于 corePoolSize时,keepAliveTime值才会起作用。也就说说,只有在线程池线程数量超出corePoolSize了。我们才会把超时的空闲线程给停止掉。否则就保持线程池中有 corePoolSize 个线程就可以了。


Unit:参数keepAliveTime的时间单位,就是 TimeUnit类当中的几个属性。
如下图:

java多线程(四)-自定义线程池 _ JavaClub全栈架构师技术笔记

 

workQueue:用来存储待执行任务的队列,不同的线程池它的队列实现方式不同(因为这关系到排队策略的问题)比如有以下几种
        ArrayBlockingQueue:基于数组的队列,创建时需要指定大小。
        LinkedBlockingQueue:基于链表的队列,如果没有指定大小,则默认值是 Integer.MAX_VALUE。(newFixedThreadPool和newSingleThreadExecutor使用的就是这种队列)。
       SynchronousQueue:这种队列比较特殊,因为不排队就直接创建新线程把任务提交了。(newCachedThreadPool使用的就是这种队列)。

threadFactory:线程工厂,用来创建线程。

Handler:拒绝执行任务时的策略,一般来讲有以下四种策略,
       (1) ThreadPoolExecutor.AbortPolicy 丢弃任务,并抛出 RejectedExecutionException 异常。
       (2) ThreadPoolExecutor.CallerRunsPolicy:该任务被线程池拒绝,由调用 execute方法的线程执行该任务。
       (3) ThreadPoolExecutor.DiscardOldestPolicy : 抛弃队列最前面的任务,然后重新尝试执行任务。
       (4) ThreadPoolExecutor.DiscardPolicy,丢弃任务,不过也不抛出异常。

 

 

看一个demo ,示例代码地址:src/thread_runnable/CustomThreadPool.java

 1 class CustomTask implements Runnable{
 2     private  int id;
 3     public CustomTask(int id) {
 4         this.id = id;
 5     }
 6 
 7     @Override
 8     public void run() {
 9         // TODO Auto-generated method stub
10         System.out.println("#" + id + "   threadId=" + Thread.currentThread().getName() );
11         try {
12             TimeUnit.MILLISECONDS.sleep(100);
13         }catch(InterruptedException e){
14             e.printStackTrace();
15         }
16     }
17     
18 }
19 
20 public class CustomThreadPool {
21     public static void main(String[] args) {
22         // TODO Auto-generated method stub
23             BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
24             ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 60, TimeUnit.MICROSECONDS, queue);
25             
26             for (int i=0; i<7; i++){
27                 Runnable task = new CustomTask(i);
28                 pool.execute(task);
29             }
30             
31             pool.shutdown();
32     }
33 
34 }

 


输出结果: 

java多线程(四)-自定义线程池 _ JavaClub全栈架构师技术笔记

 

 

从这个例子,可以看出,虽然我们有7个任务,但是实际上,只有三个线程在运行。
那么当我们提交任务给线程池之后,它的处理策略是什么呢?

java多线程(四)-自定义线程池 _ JavaClub全栈架构师技术笔记

 

 

(1),如果当前线程池线程数目小于 corePoolSize(核心池还没满呢),那么就创建一个新线程去处理任务。
(2),如果核心池已经满了,来了一个新的任务后,会尝试将其添加到任务队列中,如果成功,则等待空闲线程将其从队列中取出并且执行,如果队列已经满了,则继续下一步。
(3),此时,如果线程池线程数量 小于 maximumPoolSize,则创建一个新线程执行任务,否则,那就说明线程池到了最大饱和能力了,没办法再处理了,此时就按照拒绝策略来处理。(就是构造函数当中的Handler对象)。

(4),如果线程池的线程数量大于corePoolSize,则当某个线程的空闲时间超过了keepAliveTime,那么这个线程就要被销毁了,直到线程池中线程数量不大于corePoolSize为止。

 

 

举个通俗易懂的例子,公司要设立一个项目组来处理某些任务,hr部门给的人员编制是10个人(corePoolSize)。同时给他们专门设置了一间有15个座位(maximumPoolSize)的办公室。最开始的时候来了一个任务,就招聘一个人。就这样,一个一个的招聘,招满了十个人,不断有新的任务安排给这个项目组,每个人也在不停的接任务干活。不过后来任务越来越多,十个人无法处理完了。其他的任务就只能在走廊外面排队了。后来任务越来越多,走廊的排队队伍也挤不下。然后只好找找一些临时工来帮助完成任务。因为办公室只有15个座位,所以它们最多也就只能找5个临时工。可是任务依旧越来越多,根本处理不完,那没办法,这个项目组只好拒绝再接新任务。(拒绝的方式就是 Handler),最后任务渐渐的少了,大家都比较清闲了。所以就决定看大家表现,谁表现不好,谁就被清理出这个办公室(空闲时间超过 keepAliveTime),直到 办公室只剩下10个人(corePoolSize),维持固定的人员编制为止。


关于线程池,ThreadPoolExecutor还提供了一些需要注意的方法:

(1) shutdown(),平滑的关闭线程池。(如果还有未执行完的任务,就等待它们执行完)。
(2) shutdownNow()。简单粗暴的关闭线程池。(没有执行完的任务也直接关闭)。
(3) setCorePoolSize()。设置/更改核心池的大小。
(4) setMaximumPoolSize(),设置/更改线程池中最大线程的数量限制。

 

 

这几篇java多线程文章的demo代码下载地址 http://download.csdn.net/detail/yaowen369/9786452

 

-------
作者: www.yaoxiaowen.com
github: https://github.com/yaowen369

作者:eleven_yw
来源链接:https://www.cnblogs.com/yaoxiaowen/p/6576898.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/68631.html

分享给朋友:

“java多线程(四)-自定义线程池” 的相关文章

深入理解 Java 并发锁

深入理解 Java 并发锁

📦 本文以及示例源码已归档在 javacore 一、并发锁简介 确保线程安全最常见的做法是利用锁机制(Lock、sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的...

Java 日志框架详解

Java 日志框架详解

1. JUL学习 JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用。 1.1 架构介绍 Loggers...

java之整数的分解可以理解为倒序输出

Scanner in=new Scanner(System.in); int number ; number=in.nextInt(); int result=0; do{ int diget=number%10;...

java高级

java高级

  Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。下...

java 实现图片压缩

转载https://www.cnblogs.com/strongmore/p/14158639.html 添加依赖 <dependency> <groupId>net.coobird</groupId> <artifa...

狂神Java基础总结

狂神Java基础总结

Java基础 Java学习的正确打开方式 学习是一个过程,是一个不断累积、不断沉淀、不断总结、善于传达自己的个人见解以及乐于分享的过程。 链接:https://blog.csdn.net/qq_44543508/article/details/102651841...

图解 & 深入浅出 JavaWeb:Servlet必会必知

图解 & 深入浅出 JavaWeb:Servlet必会必知

Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆  &...

Java 干货之深入理解Java泛型

Java 干货之深入理解Java泛型

一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用多中类型的代码,这种刻板的限制对代码得束缚会就会很大。 ---《Thinking in Java》 泛型大家都接触的不少,但是由于Java 历史的原因,Java 中的泛型一直...

浅谈Java中的hashcode方法

 浅谈Java中的hashcode方法   哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率。在Java的Object类中有一个方法: public native int hashCode();   根据这个...

Java 容器 & 泛型:五、HashMap 和 TreeMap的自白

Java 容器 & 泛型:五、HashMap 和 TreeMap的自白

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket Java 容器的文章这次应该是最后一篇了:Java 容器 系列。 今天泥瓦匠聊下 Maps。 一、Map回顾  ...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。