java多线程之ThreadPoolExecutor
ThreadPoolExecutor类 简介
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。
ThreadPoolExecutor 的4个构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
注意: 几个参数的大小范围,corePoolSize >= 0,maximumPoolSize >= 1 ,keepAliveTime >= 0(keepAliveTime为0时,表示线程永久存活,即使空闲很长时间,也不会撤销)
线程池配置的各种参数以及策略
1. corePoolSize (核心池的大小)与 maximumPoolSize(线程池最大线程数)
- ThreadPoolExecutor 将根据 corePoolSize 和 maximumPoolSize 设置的边界自动调整池大小。
- 池中线程的创建策略。 当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。
- 数量固定的线程池。 如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。
- 线程池数量任意。 如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。
- 动态更改大小。 在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。
2. 线程创建的时机
- 默认情况下,即使核心线程最初只是在新任务到达时才创建和启动的
- 也可以使用方法 prestartCoreThread() 或 prestartAllCoreThreads() 对其进行动态重写。如果构造带有非空队列的池,则可能希望预先启动线程。
3. 线程创建的工厂方法 ThreadFactory
- 使用 ThreadFactory 创建新线程。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
4. 保持活动时间 keepAliveTime
- 如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止,直到池中的数量减少到核心数。这提供了当池处于非活动状态时减少资源消耗的方法。
- 默认情况下,保持活动策略只在有多于 corePoolSizeThreads 的线程时应用。但是只要 keepAliveTime 值非 0,allowCoreThreadTimeOut(boolean) 方法调用后,也可将此超时策略应用于核心线程。
5. BlockingQueue 任务队列
- 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
- 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
- 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
- 任务队列的策略
- 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
- 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
- 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
6. 被拒绝的任务的处理策略
当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:如果执行程序尚未关闭,丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。即直接在 execute 方法的调用线程中运行被拒绝的任务;
7. 队列维护
- 方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。
8. 钩子(hook)方法
此类提供 protected 可重写的 beforeExecute(Runnable) 和 afterExecute(Runnable, Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。

Executors 中提供了三种常用的ThreadPoolExecutor的创建:
1. FixedThreadPool 固定线程池
固定线程池 的线程数量是固定的,由传入的参数决定。线程 keepAliveTime 为0,即不会因为空闲超时而关闭线程,同时队列是无边界的队列,不会发生任务丢弃。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. SingleThreadPoolExcutor 单线程池
单线程池中线程数量固定为1.
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3. CachedThreadPool 缓存线程池
缓存线程池的核心线程corePoolSize 数量为0,但是池中的最大线程数是 无边界。空闲超时为60s,队列用了SynchronousQueue,即任务是立即交付运行的。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
参考:Executor框架(三)线程池详细介绍与ThreadPoolExecutor
java多线程之ThreadPoolExecutor的更多相关文章
- Java多线程之ThreadPoolExecutor详解使用
1.概述 我将讲解JAVA原生线程池的基本使用,并由此延伸出JAVA中和线程管理相关的类结构体系,然后我们详细描述JAVA原生线程池的结构和工作方式 2.为什么要使用线程池 前文我们已经讲到,线程是一 ...
- JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...
- Java多线程之ConcurrentSkipListMap深入分析(转)
Java多线程之ConcurrentSkipListMap深入分析 一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...
- JAVA多线程之wait/notify
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...
- JAVA多线程之volatile 与 synchronized 的比较
一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...
- java多线程之yield,join,wait,sleep的区别
Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...
- Java多线程之Runnable与Thread
Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...
- java多线程之wait和notify协作,生产者和消费者
这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...
- Java——多线程之Lock锁
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...
随机推荐
- 算法实战-OJ之旅
算法虽然不是特别简单,但没有你想象中的那么难. Sort Array By Parity easy AC-17ms. 按照<算法导论>排序一章的一些概念,第二种可以称为是原址的(in-pl ...
- django drf框架中的user验证以及JWT拓展的介绍
登录注册是几乎所有网站都需要去做的接口,而说到登录,自然也就涉及到验证以及用户登录状态保存,最近用DRF在做的一个关于网上商城的项目中,引入了一个拓展DRF JWT,专门用于做验证和用户状态保存.这个 ...
- 全球十大OTA 谁能有一席之地?
全球十大OTA 谁能有一席之地? http://www.traveldaily.cn/article/78381/1 2014-03-05 来源:i黑马 随着旅游行业日新月异的发展,在线旅游网站的出现 ...
- 单机版ZooKeeper的安装教程
之前一直没有时间去整理,现在抽出几分钟时间整理以下,有问题的在评论区留言即可. 前期准备JDK环境(ZK需要jdk进行编译,本文以jdk1.8.0_211为例).Linux系统(本文以Centos7为 ...
- hadoop2.7之作业提交详解(上)
根据wordcount进行分析: import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; impo ...
- 纯前端下载pdf链接文件,而不是打开预览的解决方案
纯前端下载pdf链接文件,而不是打开预览的解决方案 一,介绍与需求 1.1,介绍 XMLHttpRequest 用于在后台与服务器交换数据.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行 ...
- django前后端分离部署
部署静态文件: 静态文件有两种方式1:通过django路由访问2:通过nginx直接访问 方式1: 需要在根目录的URL文件中增加,作为入口 url(r'^$', TemplateView.as_vi ...
- spring-boot-plus后台快速开发脚手架之代码生成器使用(十)
spring-boot-plus 代码生成 Generator 代码生成内容 spring-boot-plus在mybatis-plus基础上,新增param/vo等模板 拓展controller/s ...
- TensorFlow Object Detection API 迁移学习
https://blog.csdn.net/ctwy291314/article/details/80999645 https://www.cnblogs.com/gmhappy/p/9472361. ...
- Servlet 获取 数组id进行批量删除
把获取的复选框选中的 id(一般来说都是根据id 进行批量删除的) 从jsp页面 传值到Servlet中 jsp点击事件中: var array=[]; //先声明一个数组变量 var ids=$( ...