`

Thread详解四(线程池)

 
阅读更多
 

Thread详解四(线程池)

分类: 读书笔记之java 462人阅读 评论(0) 收藏 举报

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

 

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。下面这张图完整描述了线程池的类体系结构。

几个比较重要的类

ExecutorService:真正的线程池接口。
ScheduledExecutorService能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
ThreadPoolExecutorExecutorService的默认实现。

 

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

缓存型池子通常用于执行一些生存期很短的异步型任务
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

 

官方的API中建议大家尽量使用上述的工厂方法。但是如果你想定制更灵活的Executor,可以通过ThreadPoolExcutor类,如果甚有其他的需求,可以去参照ThreadPoolExcutor类的源码

线程池类为 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)方法欲添加到线程池时: 
a,如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
b,如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
c,如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
d,如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
e,当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

 

unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、
MICROSECONDS、
MILLISECONDS、
SECONDS。

 

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。
A.        在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
B.        在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
C.        在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
D.        在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

 

下面看看具体的几个工厂方法的实现,

 

[java] view plaincopy
 
  1. public static ExecutorService newFixedThreadPool(int nThreads) {  
  2.         return new ThreadPoolExecutor(nThreads, nThreads,  
  3.                                       0L, TimeUnit.MILLISECONDS,  
  4.                                       new LinkedBlockingQueue<Runnable>());  
  5.     }  
相同的corePoolSize和maximumPoolSize的大小,未设置keep alive,BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。

 

 

 

[java] view plaincopy
 
  1. public static ExecutorService newSingleThreadExecutor() {  
  2.         return new FinalizableDelegatedExecutorService  
  3.             (new ThreadPoolExecutor(11,  
  4.                                     0L, TimeUnit.MILLISECONDS,  
  5.                                     new LinkedBlockingQueue<Runnable>()));  
  6.     }  
同上一个比,size都设置为1

 

 

 

[java] view plaincopy
 
  1. public static ExecutorService newCachedThreadPool() {  
  2.         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  
  3.                                       60L, TimeUnit.SECONDS,  
  4.                                       new SynchronousQueue<Runnable>());  
  5.     }  

maximumPoolSize大小为big,其次BlockingQueue的选择上使用SynchronousQueue。简单说:该QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。比如,我先添加一个元素,接下来如果继续想尝试添加则会阻塞,直到另一个线程取走一个元素,反之亦然。(想到什么?就是缓冲区为1的生产者消费者模式^_^)


先从BlockingQueue<Runnable> workQueue这个入参开始说起。在JDK中,其实已经说得很清楚了,一共有三种类型的queue。以下为引用:(我会稍微修改一下,并用红色突出显示) 
 
所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。(什么意思?如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)
如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
先不着急举例子,因为首先需要知道queue上的三种类型。

排队有三种通用策略:
直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

 

总结:
ThreadPoolExecutor的使用还是很有技巧的。
使用无界queue可能会耗尽系统资源。
使用有界queue可能不能很好的满足性能,需要调节线程数和queue大小
线程数自然也有开销,所以需要根据不同应用进行调节。

 

参考:http://www.cnblogs.com/jersey/archive/2011/03/30/2000231.html

http://dongxuan.iteye.com/blog/901689

分享到:
评论

相关推荐

    线程池详解:线程池七大核心参数、线程池工作原理、线程池的创建方式、线程池的拒绝策略、如何合理分配线程池大小

    四、线程池的拒绝策略 1. AbortPolicy:默认策略,抛出RejectedExecutionException异常,终止执行。 2. CallerRunsPolicy:调用者运行,主线程直接执行被拒绝的任务。 3. DiscardPolicy:丢弃策略,默默丢弃被拒绝...

    android(java)四大线程池详解

    本文将详细介绍四种由 `Executors` 工具类提供的线程池类型及其特点。 1. **new Thread 的弊端** - 性能问题:每次创建新线程都会消耗资源,频繁创建和销毁线程会降低效率。 - 管理困难:无统一管理可能导致线程...

    Java线程池技术详解

    【Java线程池技术详解】 线程池是Java并发编程中的一个重要概念,它是一种线程使用模式,旨在优化线程的管理和使用,提高系统资源的利用率。线程池的引入是为了应对频繁创建和销毁线程所带来的性能开销,因为创建和...

    线程池的原理与实现详解

    ### 线程池的原理与实现详解 #### 一、线程池的简介 线程池技术在现代软件开发中扮演着至关重要的角色,尤其是在处理高并发场景下的任务调度时。传统上,我们通常通过在需要时创建新线程的方式来处理任务。然而,...

    java线程池详解文档

    ### Java线程池详解 #### 一、线程池概念 线程池是一种多线程处理形式,它能够高效地管理多个线程的生命周期,并在处理任务时避免频繁创建和销毁线程所带来的开销。线程池允许我们将任务(通常是实现了`Runnable`...

    java线程池实例详细讲解

    3. **线程工厂(Thread Factory)**:线程池可以使用自定义的线程工厂来创建新的工作线程,这允许我们在创建线程时进行特殊的初始化操作,比如设置线程名称、优先级等。 4. **拒绝策略(Rejection Handler)**:当...

    SpringBoot线程池详解含完整示例(值得珍藏)

    - threadFactory:自定义线程创建方式。 - handler:拒绝策略,用于处理线程池无法接受的新任务。 在Spring Boot中,线程池的配置可以通过属性配置(application.properties或application.yml)、Java配置(@Bean...

    详解python中的线程与线程池

    ### 详解 Python 中的线程与线程池 在探讨 Python 中线程与线程池的概念之前,我们先了解一些基本概念。 #### 进程与线程的基本概念 **进程**(Process):进程是操作系统资源分配的基本单位,也是用户程序的一次...

    详解Java线程池的增长过程

    Java线程池是Java并发编程中非常重要的一个概念,它允许程序管理多个并发执行的任务,有效地利用系统资源并控制最大并发度。在Java中,线程池的实现主要依赖于`java.util.concurrent.ThreadPoolExecutor`类。下面...

    详解Java线程池和Executor原理的分析

    2.更加方便和透明的实现计算资源控制:Java线程池提供了FixedThreadPool,可以使用它实现线程最大数目的控制,避免每来一个任务就新建一个Thread来运算,导致程序资源难以控制。 Java线程池的实现 Java的线程池有...

    java线程池的使用方式

    #### 四、ThreadPoolExecutor详解 `ThreadPoolExecutor`是`ExecutorService`的一个具体实现,也是构建自定义线程池的基础类。它提供了丰富的配置选项来满足各种复杂的线程池需求。 ```java public ...

    线程池java

    #### 四、如何合理配置线程池的大小 合理配置线程池大小是非常重要的,这直接影响到应用程序的性能。配置线程池时应考虑以下几个因素: 1. **任务特性**:任务的CPU密集型还是IO密集型?如果是CPU密集型任务,则...

    Java四种线程池的使用详解

    Java四种线程池的使用详解 Java四种线程池的使用详解是Java多线程编程中的一种重要概念。Java通过Executors提供四种线程池,分别为newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool和...

    Java中四种线程池的使用示例详解

    【Java中的四种线程池详解】 Java中的线程池提供了高效管理并发任务的机制,它通过维护一组可重用的工作线程来减少线程创建和销毁的开销。本文将详细介绍Java中的四种线程池:FixedThreadPool、CachedThreadPool、...

    java 线程池

    ### Java线程池详解 #### 一、线程与线程池的概念 在Java中,线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程(例如某个Java应用)至少有一个线程,如果线程...

    LINUX_c++线程池

    ### LINUX_c++线程池详解 #### 一、线程池的重要性及背景 现代网络服务,如Web服务器、电子邮件服务器和数据库服务器等,通常需要在短时间内处理大量的连接请求。为了应对这种需求,传统的多线程解决方案通常是每...

    一个完整的线程池的实例

    #### 主要方法详解 1. **`run` 方法**: - 参数:`runner`(`Runnable`类型)——要执行的任务。 - 功能:提交任务到线程池进行执行。 - 实现逻辑: - 如果传入的`runner`为空,则抛出`NullPointerException`。...

    Tomcat6.0共享线程池配置

    ### Tomcat 6.0 共享线程池配置详解 #### 一、引言 在探讨Tomcat 6.0共享线程池配置之前,我们先了解下什么是线程池以及为什么要在Tomcat服务器中配置共享线程池。线程池是一种用于管理多个线程并重用它们的技术,...

    JAVA线程池例子

    3. **线程池参数详解**: - `corePoolSize`:线程池的基本大小,当线程数小于这个值时,即使工作队列未满,也会创建新线程。 - `maximumPoolSize`:线程池最大大小,超过这个数量的任务将被放入工作队列。 - `...

    c#线程池应用

    ### C#线程池应用详解 #### 一、引言 在现代软件开发中,多线程编程是一项不可或缺的技术,特别是在高性能和响应性要求较高的应用中。本文将深入探讨Microsoft .NET框架中线程池的概念及其应用。我们将讨论为什么...

Global site tag (gtag.js) - Google Analytics