Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。它很灵活,但使用起来也比较复杂,本文就对其做一个介绍。
 
 首先是构造函数。以最简单的构造函数为例:
 [java]
 public ThreadPoolExecutor(   
             int corePoolSize,   
             int maximumPoolSize,   
             long keepAliveTime,   
             TimeUnit unit,   
             BlockingQueue workQueue)  
     public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
 看起来挺复杂的。这里介绍一下。
 
corePoolSize 指的是保留的线程池大小。
 maximumPoolSize 指的是线程池的最大大小。
 keepAliveTime 指的是空闲线程结束的超时时间。
 unit 是一个枚举,表示 keepAliveTime 的单位。
 workQueue 表示存放任务的队列。
 我们可以从线程池的工作过程中了解这些参数的意义。线程池的工作过程如下:
 
 1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
 2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
     a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
     b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
     c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
     d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
 3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
 4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
 这样的过程说明,并不是先加入任务就一定会先执行。假设队列大小为 10,corePoolSize 为 3,maximumPoolSize 为 6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 1、2、3,然后任务 4~13 被放入队列。这时候队列满了,任务 14、15、16 会被马上执行,而任务 17~20 则会抛出异常。最终顺序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。下面是一个线程池使用的例子:
 
[java]
 public static void main(String[] args) {   
     BlockingQueue queue = new LinkedBlockingQueue();   
     ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);   
     
     for (int i = 0; i < 20; i++) {   
         executor.execute(new Runnable() {   
     
             public void run() {   
                 try {   
                     Thread.sleep(1000);   
                 } catch (InterruptedException e) {   
                     e.printStackTrace();   
                 }   
                 System.out.println(String.format("thread %d finished", this.hashCode()));   
             }   
         });   
     }   
     executor.shutdown();   
 }
 对这个例子的说明如下:
 1、BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于没有大小限制。这样的话,因为队列不会满,所以 execute() 不会抛出异常,而线程池中运行的线程数也永远不会超过 corePoolSize 个,keepAliveTime 参数也就没有意义了。
 2、shutdown() 方法不会阻塞。调用 shutdown() 方法之后,主线程就马上结束了,而线程池会继续运行直到所有任务执行完才会停止。如果不调用 shutdown() 方法,那么线程池会一直保持下去,以便随时添加新的任务。
 到这里对于这个线程池还只是介绍了一小部分。ThreadPoolExecutor 具有很强的可扩展性,不过扩展它的前提是要熟悉它的工作方式。
 
 java.util.concurrent.ThreadPoolExecutor 类提供了丰富的可扩展性。你可以通过创建它的子类来自定义它的行为。例如,我希望当每个任务结束之后打印一条消息,但我又无法修改任务对象,那么我可以这样写:
 
 [java]
 ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
     @Override
     protected void afterExecute(Runnable r, Throwable t) {
         System.out.println("Task finished.");
     }
 };<span style="font-family: Arial; text-indent: 2em;"> </span>
 除了 afterExecute 方法之外,ThreadPoolExecutor 类还有 beforeExecute() 和 terminated() 方法可以重写,分别是在任务执行之前和整个线程池停止之后执行。
 
 
 除了可以添加任务执行前后的动作之外, ThreadPoolExecutor 还允许你自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。 ThreadPoolExecutor 提供 4 个现有的策略,分别是:
 
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
 ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
 ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
 ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。
 这里是一个例子:
 [java]
 ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);
     executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
 除此之外,你也可以通过实现 RejectedExecutionHandler 接口来编写自己的策略。下面是一个例子:
 
 [java]
 ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
         new RejectedExecutionHandler() {
             public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                 System.out.println(String.format("Task %d rejected.", r.hashCode()));
             }
         }
 );

Java自带的线程池ThreadPoolExecutor详细介绍说明和实例运用相关推荐

  1. Java自带的线程池Executors.newFixedThreadPool

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...

  2. Java并发编程之线程池ThreadPoolExecutor解析

    线程池存在的意义 平常使用线程即new Thread()然后调用start()方法去启动这个线程,但是在频繁的业务情况下如果在生产环境大量的创建Thread对象是则会浪费资源,不仅增加GC回收压力,并 ...

  3. 深入理解Java自带的线程池和缓冲队列

    https://www.cnblogs.com/xiguadadage/p/10243332.html

  4. Python 线程池 ThreadPoolExecutor(一) - Python零基础入门教程

    目录 一.Python 线程池前言 二.Python 线程池原理 三.Python 线程池 ThreadPoolExecutor 函数介绍 四.Python 线程池 ThreadPoolExecuto ...

  5. Java 线程池 ThreadPoolExecutor 八种拒绝策略浅析

    前言 谈到 Java 的线程池最熟悉的莫过于 ExecutorService 接口了,jdk1.5 新增的 java.util.concurrent 包下的这个 api,大大的简化了多线程代码的开发. ...

  6. Java并发—线程池ThreadPoolExecutor基本总结

    原文作者:Matrix海子 原文地址:Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线 ...

  7. Java线程池—ThreadPoolExecutor

    2019独角兽企业重金招聘Python工程师标准>>> 为什么要使用线程池创建线程?     使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题 ...

  8. 线程池ThreadPoolExecutor详解(整理详细)

    ThreadPoolExecutor 1.什么是线程池? (首先要理解什么是线程) 线程池,thread pool,是一种线程使用模式,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务. 通 ...

  9. 【Java 并发编程】线程池机制 ( ThreadPoolExecutor 线程池构造参数分析 | 核心线程数 | 最大线程数 | 非核心线程存活时间 | 任务阻塞队列 )

    文章目录 前言 一.ThreadPoolExecutor 构造参数 二.newCachedThreadPool 参数分析 三.newFixedThreadPool 参数分析 四.newSingleTh ...

最新文章

  1. 有必要总结一下:matlab图像灰度调整——imadjust函数的使用
  2. python领域有什么用-Python都能应用到哪些领域?具体就业职位有什么?
  3. Fastcgi 协议解析及 getpost 使用实例
  4. UA MATH571A 多元线性回归I 模型设定与推断
  5. 关注书籍和教程(更新中)
  6. Android轮播图实现图片圆角,Android开发实现图片圆角的方法
  7. linux下c语言 双向链表
  8. LoadRunner本机录制http协议程序遇到的问题以及解决方法
  9. abc类ip地址_通信网络的IP地址分配原理
  10. 数据中台技术及业务发展史与未来趋势展望
  11. Goalng小demo二:客户信息关系系统
  12. Deal with relational data using libFM with blocks
  13. Node.js学习之路04——Buffer对象与字符串
  14. 进程调度算法的模拟实现
  15. 网页测速 php,好用实用的站长测速工具网站大全
  16. 《大数据工程师 面经 自己整理 面试题1--》
  17. 新手电脑硬件软件故障解答(二)
  18. shell脚本编写简易教程
  19. 用Bootstrap写一份简历
  20. 严格模式与混杂模式-如何触发这两种模式,区分它们有何意义

热门文章

  1. LiveVideoStack线上交流分享 ( 四 ) —— 面向QoE的感知视频编码
  2. ffplay.c学习-1-框架及数据结构
  3. CentOS 7 DIG命令工具打上EDNS补丁
  4. 记一种数据库水平扩展的技巧
  5. Storm 02_Storm 计算模型
  6. How to extend unallocated space to an existing partition on linux? | 如何在 linux 上扩展已有分区至未分配空间?
  7. leetcode 1721. Swapping Nodes in a Linked List | 1721. 交换链表中的节点(Java)
  8. leetcode 287. Find the Duplicate Number | 287. 寻找重复数(判断链表是否有环,并找到环的起点)
  9. 【Python】简单的apscheduler定时任务
  10. netty系列之:自定义编码和解码器要注意的问题