`

线程池(java.util.concurrent.ThreadPoolExecutor)的使用

 
阅读更多
一、简介

线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler)


corePoolSize: 线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

handler: 线程池对拒绝任务的处理策略

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时:

l  如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

l  如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。

l  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

l  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

l  当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:

NANOSECONDS、

MICROSECONDS、

MILLISECONDS、

SECONDS。


workQueue常用的是:java.util.concurrent.ArrayBlockingQueue

handler有四个选择

ThreadPoolExecutor.AbortPolicy()

抛出java.util.concurrent.RejectedExecutionException异常

ThreadPoolExecutor.CallerRunsPolicy()

重试添加当前的任务,他会自动重复调用execute()方法

ThreadPoolExecutor.DiscardOldestPolicy()

抛弃旧的任务

ThreadPoolExecutor.DiscardPolicy()

抛弃当前的任务

二、相关参考

一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包 括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。

为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、 Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调 整此类时,使用以下指导:

核心和最大池大小

ThreadPoolExecutor 将根据 corePoolSize(参见 getCorePoolSize())和 maximumPoolSize(参见 getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

按需构造

默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法 prestartCoreThread() 或 prestartAllCoreThreads() 对其进行动态重写。

创建新线程

使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。

保持活动时间

如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(参见 getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方 法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。

排队

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

A.        如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

B.        如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

C.        如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此 会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以 最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

被拒绝的任务

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

A.        在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。

B.        在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

C.        在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。

D.        在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

挂钩方法

此类提供 protected 可重写的 beforeExecute(java.lang.Thread, java.lang.Runnable) 和 afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。

如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

队列维护

方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

一、例子

创建 TestThreadPool 类:


import java.util.concurrent.ArrayBlockingQueue;   
import java.util.concurrent.ThreadPoolExecutor;   
import java.util.concurrent.TimeUnit;   

public class TestThreadPool {   

private static int produceTaskSleepTime = 2;   

private static int produceTaskMaxNumber = 10;   

public static void main(String[] args) {   

// 构造一个线程池   
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,   
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),   
new ThreadPoolExecutor.DiscardOldestPolicy());   

for (int i = 1; i <= produceTaskMaxNumber; i++) {   
try {   
String task = "task@ " + i;   
System.out.println("创建任务并提交到线程池中:" + task);   
threadPool.execute(new ThreadPoolTask(task));   

Thread.sleep(produceTaskSleepTime);   
} catch (Exception e) {   
e.printStackTrace();   
}   
}   
}   
}  
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPool {

 private static int produceTaskSleepTime = 2;

private static int produceTaskMaxNumber = 10;

 public static void main(String[] args) {

  // 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());

  for (int i = 1; i <= produceTaskMaxNumber; i++) {
try {
String task = "task@ " + i;
System.out.println("创建任务并提交到线程池中:" + task);
threadPool.execute(new ThreadPoolTask(task));

    Thread.sleep(produceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}



创建 ThreadPoolTask类:

import java.io.Serializable;   

public class ThreadPoolTask implements Runnable, Serializable {   

private Object attachData;   

ThreadPoolTask(Object tasks) {   
this.attachData = tasks;   
}   

public void run() {   

System.out.println("开始执行任务:" + attachData);   

attachData = null;   
}   

public Object getTask() {   
return this.attachData;   
}   
}  
import java.io.Serializable;

public class ThreadPoolTask implements Runnable, Serializable {

 private Object attachData;

 ThreadPoolTask(Object tasks) {
this.attachData = tasks;
}

 public void run() {

System.out.println("开始执行任务:" + attachData);

attachData = null;
}

 public Object getTask() {
return this.attachData;
}
}


执行结果:

               创建任务并提交到线程池中:task@ 1

开始执行任务:task@ 1

创建任务并提交到线程池中:task@ 2

开始执行任务:task@ 2

创建任务并提交到线程池中:task@ 3

创建任务并提交到线程池中:task@ 4

开始执行任务:task@ 3

创建任务并提交到线程池中:task@ 5

开始执行任务:task@ 4

创建任务并提交到线程池中:task@ 6

创建任务并提交到线程池中:task@ 7

创建任务并提交到线程池中:task@ 8

开始执行任务:task@ 5

开始执行任务:task@ 6

创建任务并提交到线程池中:task@ 9

开始执行任务:task@ 7

创建任务并提交到线程池中:task@ 10

开始执行任务:task@ 8

开始执行任务:task@ 9

开始执行任务:task@ 10

   
<bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">   
<property name="corePoolSize" value="50" />   
<property name="maxPoolSize" value="100"/>   
<property name="queueCapacity" value="200000"/>   
<property name="keepAliveSeconds" value="3000"/>   
<property name="rejectedExecutionHandler">   
<bean class="java.util.concurrent.ThreadPoolExecutor$DiscardPolicy"/>   
</property>  
</bean> 


ThreadPoolExecutor配置

、ThreadPoolExcutor为一些Executor提供了基本的实现,这些Executor是由Executors中的工厂 newCahceThreadPool、newFixedThreadPool和newScheduledThreadExecutor返回的。 ThreadPoolExecutor是一个灵活的健壮的池实现,允许各种各样的用户定制。

、线程的创建与销毁

1、核心池大小、最大池大小和存活时间共同管理着线程的创建与销毁。

2、核心池的大小是目标的大小;线程池的实现试图维护池的大小;即使没有任务执行,池的大小也等于核心池的大小,并直到工作队列充满前,池都不会创建更多的线程。如果当前池的大小超过了核心池的大小,线程池就会终止它。

3、最大池的大小是可同时活动的线程数的上限。

4、如果一个线程已经闲置的时间超过了存活时间,它将成为一个被回收的候选者。

5、newFixedThreadPool工厂为请求的池设置了核心池的大小和最大池的大小,而且池永远不会超时

6、newCacheThreadPool工厂将最大池的大小设置为Integer.MAX_VALUE,核心池的大小设置为0,超时设置为一分钟。这样创建了无限扩大的线程池,会在需求量减少的情况下减少线程数量。

、管理

1、 ThreadPoolExecutor允许你提供一个BlockingQueue来持有等待执行的任务。任务排队有3种基本方法:无限队列、有限队列和同步移交。

2、 newFixedThreadPool和newSingleThreadExectuor默认使用的是一个无限的 LinkedBlockingQueue。如果所有的工作者线程都处于忙碌状态,任务会在队列中等候。如果任务持续快速到达,超过了它们被执行的速度,队 列也会无限制地增加。稳妥的策略是使用有限队列,比如ArrayBlockingQueue或有限的LinkedBlockingQueue以及 PriorityBlockingQueue。

3、对于庞大或无限的池,可以使用SynchronousQueue,完全绕开队列,直接将任务由生产者交给工作者线程

4、可以使用PriorityBlockingQueue通过优先级安排任务。

线程池队列饱和策略

1、当一个有限队列充满后,线程池的饱和策略开始起作用。

2、ThreadPoolExecutor的饱和策略通过调用setRejectedExecutionHandler来修改。不同的饱和策略如下:

1)AbortPolicy:中止,executor抛出未检查RejectedExecutionException,调用者捕获这个异常,然后自己编写能满足自己需求的处理代码。

2)DiscardRunsPolicy:遗弃最旧的,选择丢弃的任务,是本应接下来就执行的任务。

3)DiscardPolicy:遗弃会默认放弃最新提交的任务(这个任务不能进入队列等待执行时)

4)CallerRunsPolicy:调用者运行,既不会丢弃哪个任务,也不会抛出任何异常,把一些任务推回到调用者那里,以此减缓新任务流。它不会在池线程中执行最新提交的任务,但它会在一个调用了execute的线程中执行。

3、创建一个可变长的线程池,使用受限队列和调用者运行饱和策略。

ThreadPoolExecutor executor=new ThreadPoolExecutor(N_THREADS,N_THREADS,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(CAPACITY));

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

4、当线程队列充满后,并没有预置的饱和策略来阻塞execute。但是,使用Semaphore信号量可以实现这个效果。Semaphore会限制任务注入率。

@ThreadSafe

public class BoundedExecutor{

   private final Executor exec;

   private final Semaphore semaphore;

   public BoundedExecutor(Executor exec,int bound){

       this.exec=exec;

       this.semaphore=new Semaphore(bound);

   }

 

   public void submitTask(final Runnable command) throws InterruptedException{

        semaphore.acquire();

        try{

            exec.execute(new Runnable(){

               public void run(){

                  try{

                          command.run();

                  }

                  finally{

                      semaphore.release();

                  }

               }

            });

        }catch (RejectedExecutionException e){

             semaphore.release();

        }

   }

}
分享到:
评论

相关推荐

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用

    java并发工具包 java.util.concurrent中文版用户指南pdf

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介.doc

    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介

    Java并发工具包java.util.concurrent用户指南中英文对照阅读版.pdf

    java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    线程池:java_ThreadPoolExecutor.mht

    (转)线程池:java_util_ThreadPoolExecutor 比较详细的介绍了ThreadPoolExecutor用法与属性

    Java并发工具包java.util.concurrent用户指南中英文对照阅读版

    本资源包含两个 pdf 文档,一本根据 Jakob Jenkov 最新博客 (http://tutorials.jenkov.com/java-util-concurrent/index.html) 整理的 java_util_concurrent_user_guide_en.pdf,一个中文翻译的 java_util_concurrent...

    借助Ehcache缓存框架实现对页面的缓存Demo

    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run...

    Java线程池文档

    Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用简介》[4],《Java5中的线程池实例讲解》[5],《ThreadPoolExecutor使用和思考》[6] ...

    java并发工具包详解

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    java并发包资源

    本资源包含两个 pdf 文档,一本根据 Jakob Jenkov 最新博客 (http://tutorials.jenkov.com/java-util-concurrent/index.html) 整理的 java_util_concurrent_user_guide_en.pdf,一个中文翻译的 java_util_concurrent...

    java head space.txt

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run...

    java编发编程:JUC综合讲解

    JUC(java.util.concurrent)库是 Java 标准库的一部分,提供了丰富的多线程并发工具,旨在帮助开发者编写高性能、高可伸缩性的并发程序。下面综合介绍 JUC 库的几个核心概念以及它们在并发编程中的重要性。 1. ...

    Java并发编程基础.pdf

    Java并发编程基础主要包括以下几个核心方面: ...并发工具类:掌握Java并发包java.util.concurrent中提供的各种工具类,如CountDownLatch、CyclicBarrier、Semaphore等,它们简化了并发编程的复杂性。

    关于ThreadPool抛出OOM问题

    关于ThreadPool抛出OOM问题案例为什么会OOM 案例 最近在学习Java调优,有个案例是ThraadPool导致OOM,在不了解线程池...import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public

    java线程池源码-cThreadPool:JAVA线程池源码分析与重写

    项目描述:对java.util.concurrent包下线程池相关源码进行重新实现,深入研究和学习线程池超时机制、饱和策略、生命周期等知识 ThreadPoolExecutor类下部分方法和内部类介绍: 1、Worker类: 描述:Worker类实现...

    Java并发包源码分析(JDK1.8)

    Java并发包源码分析(JDK1.8):囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue...

    Android AsyncTask 完美解析 看不懂源码你就输了

    1.简介 ...如果需要长时间保持线程运行,建议使用 java.util.concurrent 包提供的各种API。 例如{@link Executor},{@ link ThreadPoolExecutor}和{@link FutureTask}。 2.基本使用 2.1 关键API andro

    Java并发编程实战

    14.6 java.util.concurrent同步器类中的 AQS257 14.6.1 ReentrantLock257 14.6.2 Semaphore与CountDownLatch258 14.6.3 FutureTask259 14.6.4 ReentrantReadWriteLock259 第15章 原子变量与非阻塞同步机制261 ...

    python3.6.5参考手册 chm

    concurrent.futures contextlib datetime decimal distutils email encodings enum faulthandler fileinput hashlib http.client idlelib and IDLE importlib inspect json logging math ...

Global site tag (gtag.js) - Google Analytics