`

管理你的线程池(Executor)

阅读更多
    我们都知道使用线程池能够控制线程的数量,尤其是大量的“短命”线程存在时,线程池将大大降低系统消耗(内存和CPU)。不过,线程池也同样需要管理,于是我写了本篇。
首先,我们来看看管理器的整个继承关系:



    显而易见,有ThreadPoolExecutor和ScheduledThreadPoolExecutor两个实现类,当然Executor类里也有一些内部类实现了特定的功能(如class DelegatedScheduledExecutorService),我们也可以自己通过扩展这里所有的接口、抽象类、类来实现自己的特定功能,如继承ThreadPoolExecutor类,覆写beforeExecute(),让它在每个任务开始执行前执行某些操作,还有很多可扩展功能,有兴趣的朋友可以自己摸索。
    你有两种方法创建上面管理器的实例:
1、你可以用上面介绍的两个类的那这些类的实例的构造函数来创建管理器的实例,不过你要自己配置一些诸如池最大尺寸(maximumPoolSize )的参数。
2、Executors提供各种创建上面的类的实例的方法,它默认一些参数的设置。我主要介绍
这种方法中的newFixedThreadPool(int)和newCachedThreadPool()


------------newFixedThreadPool(int)------------
     创建一个默认尺寸的池,它同时运行的线程数将是固定的,如果你要让它课同时运行的最大线程数大于初始设置的那个参数,可以调用setMaximumPoolSize()来设置额外的线程来并行处理更多的任务。
     我们调用下面的方法来添加新的任务,到底Executors是如何处理的呢?

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if(poolSize>=corePoolSize|| !addIfUnderCorePoolSize(command)) {
//如果实时连接数小于corePoolSize,那么调用addIfUnderCorePoolSize()方法
            if (runState == RUNNING && workQueue.offer(command)) {
	//如果实时连接数大于了corePoolSize,那么将任务加进等待队列中。
                if (runState != RUNNING || poolSize == 0)
	//在执行workQueue.offer(command)的过程中shutdown了,确保所有的已经提交任务能够成功执行完。
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
	
                reject(command); // is shutdown or saturated
        }
    }

下面我们来看下poolSize>=corePoolSize为不同状态时两种执行方法:
 private boolean addIfUnderCorePoolSize(Runnable firstTask) {
	//首先获取本类所有同步方法的锁
        Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < corePoolSize && runState == RUNNING)
                t = addThread(firstTask);
        } finally {
            mainLock.unlock();
        }
        if (t == null)
            return false;
        t.start();
        return true;
    }
 
 private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
	//首先获取本类所有同步方法的锁
	 Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < maximumPoolSize && runState == RUNNING)
                t = addThread(firstTask);
        } finally {
            mainLock.unlock();
        }
        if (t == null)
            return false;
        t.start();
        return true;

	}
几乎完全一样,估计author Doug Lea当初也是直接copy的吧。
这两个方法都调用了
private Thread addThread(Runnable firstTask) {
        Worker w = new Worker(firstTask);
	//这里并没有区分maximumPoolSize 和corePoolSize 
	Thread t = threadFactory.newThread(w);
        if (t != null) {
            w.thread = t;
            workers.add(w);//workers并没有尺寸的限制
            int nt = ++poolSize;
	//这一步维护一个管理器使用过程中的最大尺寸,没什么好说的。
            if (nt > largestPoolSize)
                largestPoolSize = nt;
        }
        return t;
    }
于是我认为发现管理器在对待aximumPoolSize 和corePoolSize 时根本没有什么区别,可是这是不正确的,至于为什么,大家可以自己去探索!

      ThreadPoolExecutor类内部有一个:
private final HashSet<Worker> workers = new HashSet<Worker>();
其中Worker类是ThreadPoolExecutor一个内部类,实现了Runable接口。在addIfUnderMaximumPoolSize()和addIfUnderCorePoolSize()两个方法中将任务添加进这个workers[]中,这个数组维护一个正在运行的任务组,这个数组中的一个元素对应一个正在运行的线程,如果一个线程以外死亡,数组中的元素没有被移除,管理器将自动创建一个新的线程继续从头开始执行刚刚那个以外死亡的数组对应的任务。
       如此神奇?那是如何实现的?
       很简单,ThreadPoolExecutor维护的线程的run方法都是在这个loop中的,

while (task != null || (task = getTask()) != null) {
                    runTask(task);
                    task = null;
                }

如果意外死亡,task=null不执行,重新判断条件的时候再次调用runTask(task);即,死亡的是runTask(task)方法内部的run()调用而已。

      说到这里,大家应该明白了,管理器无非就是用BlockingQueue<Runnable> workQueue队列(注意这个队列是线程安全的,挺有意思)来缓冲多出来的任务,而总是有不大于maximumPoolSize(注意,这里不是corePoolSize )的线程在运行着,再有点异常死亡处理的能力而已。



--------newCachedThreadPool()--------

这个方法源码:
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
	}


    原来,就是让corePoolSize =0,maximumPoolSize=最大整数,然后设置空闲线程的存活时间为60s而已。看到这里,大家或许会冒出这样一个疑问:既然corePoolSize 是0,那么不是运行不了任何线程吗?呵呵,大家如果认真看了前面的文章就会有此疑问了。看同时刻运行的线程最大数是看参数maximumPoolSize不是corePoolSize 。
至于存活时间设置,那是很有必要,否则

while (task != null || (task = getTask()) != null) {
                    runTask(task);
                    task = null;
                }

getTask方法中从待执行任务缓冲队列中poll()任务的时候会有一个存活时间的超时机制,如果超时将返回null,这个线程将因为一系列连锁反应,最终死亡。

      好了,看似简单的Executor我砍了这么多,顺序整理的不是很好,大家将就看看吧。
总结一下,在设计这几个类的时候用到集合、同步(锁和阻塞队列)、枚举(TimeUnit)、多线程、安全控制(本文没有涉及)、工厂设计模式等等知识点,不简单哪^-^

  • 大小: 55.7 KB
分享到:
评论
6 楼 lovechenxue 2010-12-15  
最近接手的代码里面用了线程池  一直不了解它有什么优点   现在看了这篇文章全明白了  非常感谢
5 楼 javamonkey 2010-12-15  
maximumPoolSize 跟queue总类关系很大,如果是容量无限的话,maximumPoolSize 设置无效
4 楼 贾懂凯 2010-12-15  
westlifeljz 写道
楼主 你能添一下完整的代码吗? 有些代码看不懂啊。。。

写的比较仓促,建议对着源码看。
3 楼 westlifeljz 2010-12-15  
楼主 你能添一下完整的代码吗? 有些代码看不懂啊。。。
2 楼 贾懂凯 2010-12-15  
jxsgy 写道
楼主继续,写的很好啊,通俗易懂

本来就没必要用什么术语,那都是用来装逼的,一看就懂多好。会继续的……
1 楼 jxsgy 2010-12-15  
楼主继续,写的很好啊,通俗易懂

相关推荐

    springmvc配置线程池Executor做多线程并发操作的代码实例

    线程池Executor是Spring框架提供的一种线程池实现,它允许我们在应用程序中创建和管理线程池,以便实现高效的并发处理。 首先,我们需要在Spring的配置文件中添加对线程池Executor的支持。我们可以在...

    Java并发之线程池Executor框架的深入理解

    Java中的线程池Executor框架是Java并发编程中的一种常见机制,用于管理和执行异步任务。通过使用线程池,可以大大减少线程的创建和销毁开销,从而提高系统的性能和稳定性。 Executor框架是Java中的一个核心框架,...

    线程池之Executor框架.docx

    `ExecutorService`接口继承自`Executor`,提供了更丰富的管理功能,如关闭线程池、管理线程等。`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`是`ExecutorService`的两个关键实现,分别用于执行一次性任务和...

    Android Executor线程池

    在`Executor`框架中,`ExecutorService`是核心接口,它扩展了`Executor`接口并添加了一些用于管理和控制线程池的方法,如提交任务、关闭线程池等。Android开发者通常会使用`ThreadPoolExecutor`或`...

    线程池管理线程demo

    下面将详细阐述线程池的工作原理、优势以及如何在实际应用中创建和管理线程池。 1. **线程池工作原理** 线程池由一组可重用的线程组成,当有新的任务需要执行时,线程池会从已创建的线程中选择一个空闲线程来执行...

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

    Executor可以实现任务的执行、线程池的管理等功能。 ThreadPoolExecutor ThreadPoolExecutor是Java线程池的核心实现类,它提供了多种线程池的实现,包括CachedThreadPool、FixedThreadPool等。ThreadPoolExecutor...

    并发编程之Executor线程池原理与源码解读.pdf

    而ExecutorService则是Executor接口的一个重要子接口,它扩展了Executor的功能,添加了管理线程池生命周期的方法。 至于线程的实现方式,通常有两种,即Runnable和Callable。Runnable接口是执行任务的最基本的实现...

    线程池java

    3. **线程管理**:线程池会根据当前的任务负载动态调整线程的数量。当任务较少时,多余的线程会被销毁,从而避免资源浪费;当任务较多时,线程池会创建更多的线程来处理任务。 4. **拒绝策略**:当任务队列已满并且...

    深入探索:Java线程池的工作原理与实践

    在现代多线程编程中,Java线程池(Executor框架)扮演着至关重要的角色。它不仅提高了程序的性能,还有效地管理了资源。本文将深入探讨Java线程池的工作原理,并通过实际代码示例,展示如何高效地使用线程池。 ...

    Java线程池使用说明

    1. Executor:它是Java中线程池的顶级接口,定义了执行线程的抽象方法,但它本身并不直接提供线程池功能。 2. ExecutorService:是真正的线程池接口,提供了一系列方法来管理线程的生命周期以及任务的执行。 3. ...

    JDK1.5线程池源码及详细注释

    在Java并发编程中,线程池(ThreadPoolExecutor)是一个至关重要的工具,它允许开发者有效地管理线程资源,提高系统的性能和响应性。JDK 1.5引入了java.util.concurrent包,其中包含了线程池的实现,使得并发编程...

    java线程池ThreadPoolExecutor类使用详解.docx

    另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、...

    关于线程池的代码demo

    线程池的使用能够有效地管理和控制线程资源,避免频繁创建和销毁线程带来的性能开销,提高系统效率。下面将详细解释线程池的工作原理以及如何通过代码实现一个简单的线程池示例。 线程池的基本工作流程如下: 1. *...

    一个线程池封装类及例子程序

    在Java等编程语言中,线程池的实现通常基于Executor框架,允许开发者创建并管理一组可重用的工作线程。本资料提供了一个线程池的封装类,以及相关的例子程序,对于理解和实践线程池的使用具有很高的参考价值。 首先...

    Java中多线程的使用线程池.docx

    工作线程负责执行任务,任务队列用于存储待处理的任务,控制机制则负责管理线程池的大小和任务的分配。 2. **线程池的优点**: - **资源复用**:线程池中的线程可以重复使用,减少了创建和销毁线程的开销。 - **...

    Executor框架使用详解

    基于`Executor`,`ExecutorService`接口提供了更丰富的功能,如关闭线程池的`shutdown()`和`shutdownNow()`方法,以及管理和控制任务执行的方法,如`submit()`、`invokeAll()`和`invokeAny()`。`ExecutorService`...

    spark:Executor分配详解

    - **执行任务**:一旦创建完毕,Executor就会从Driver接收任务并在其线程池中执行这些任务。Executor还可以通过BlockManager缓存中间结果,以提高后续任务的执行效率。 - **回收**:当任务执行完毕或资源不再需要时...

    JAVA使用线程池查询大批量数据

    使用线程池查询大批量数据能够有效地提高并发性能,但同时需要注意资源管理和任务间的同步问题。合理的任务划分、线程池参数设置以及异常处理策略都是确保程序高效、稳定运行的关键。在实际项目中,应根据业务场景和...

    android线程池

    线程池通过ThreadPoolExecutor进行具体实现,该类提供了创建和管理线程池的能力。 二、固定线程池(FixedThreadPool) 固定线程池由java.util.concurrent.Executors类的newFixedThreadPool方法创建。它维护一个...

    java 线程池常用方法

    线程池通过`Executor`接口和`ExecutorService`接口提供了一套强大的机制,允许开发者高效地创建、管理和控制线程的执行。 1. **创建任务** 创建任务通常需要实现`Runnable`接口或`Callable`接口。`Runnable`接口...

Global site tag (gtag.js) - Google Analytics