Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

转载 2012年03月28日 14:59:24

原文:http://my.oschina.net/sunjun/blog/9017

引用

【轰隆隆】 的 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

来源:YidingHe's Blog

从 Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。它很灵活,但使用起来也比较复杂,本文就对其做一个介绍。


首先是构造函数。以最简单的构造函数为例:


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。下面是一个线程池使用的例子:


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 类提供了丰富的可扩展性。你可以通过创建它的子类来自定义它的行为。例如,我希望当每个任务结束之后打印一条消息,但我又无法修改任务对象,那么我可以这样写:

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("Task finished.");
    }
};

 

除了 afterExecute 方法之外,ThreadPoolExecutor 类还有 beforeExecute() 和 terminated() 方法可以重写,分别是在任务执行之前和整个线程池停止之后执行。



除了可以添加任务执行前后的动作之外, ThreadPoolExecutor 还允许你自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。 ThreadPoolExecutor 提供 4 个现有的策略,分别是:


ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常 
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作 
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务 
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。 

这里是一个例子:

 

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);

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

 


除此之外,你也可以通过实现 RejectedExecutionHandler 接口来编写自己的策略。下面是一个例子:

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 多线程编程之九:使用 Executors 和 ThreadPoolExecutor 实现的 Java 线程池的例子

线程池用来管理工作线程的数量,它持有一个等待被执行的线程的队列。         java.util.concurrent.Executors 提供了 java.util.concurrent.Exe...
  • defonds
  • defonds
  • 2013年08月02日 12:39
  • 23453

线程池ThreadPoolExecutor的使用和有关ThreadFactory和thread等粗解

 private static int CORE_POOL_SIZE = 5;  // 线程池最大线程数  private static int MAX_POOL_SIZE = 10...
  • daogepiqian
  • daogepiqian
  • 2016年01月27日 14:40
  • 1780

java并发编程—— 线程池原理 详解 ThreadPoolExecutor

为什么要使用线程池 降低资源消耗: 通过重复利用线程,减少线程的创建销毁损耗的资源 提高响应速度: 任务到达时,不用重新创建线程,之间可以使用已经创建好的线程执行 提高线程的可管理性 线程池实现原...
  • lemon89
  • lemon89
  • 2016年03月26日 17:42
  • 1492

Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

从 Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadPoolExecutor 就是这样的线...
  • englishbage
  • englishbage
  • 2012年09月29日 11:23
  • 1390

Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadPoolExecutor 就是这样的线程池...
  • zxae86
  • zxae86
  • 2015年07月14日 20:31
  • 227

Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

来源:YidingHe's Blog 从 Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.Thr...
  • cookieweb
  • cookieweb
  • 2012年03月20日 08:24
  • 483

Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

来源:YidingHe's Blog从 Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程。 java.util.concurrent.ThreadP...
  • nodie
  • nodie
  • 2011年03月29日 16:07
  • 328

Java自带的线程池ThreadPoolExecutor

一、线程池引入  Java的线程池是Java5.0以后的新功能,它让开发者更易开发高效的多线程程序,也让多线程程序的性能大大提高。Java提供的关于线程池的API是基于原有线程API的,只是用另外一种...
  • wtyvhreal
  • wtyvhreal
  • 2015年06月04日 22:43
  • 595

Java线程池的详细介绍

之前看到别人写的多线程方法,自己用得不知所以然,感觉很强大同时自己又很好奇。Java的多线程是高阶必须学习的知识,现在把网络上查询到的理论知识特别整理一下。 简单介绍 接下来是对线程池的特别介绍:...
  • yang_best
  • yang_best
  • 2014年11月21日 14:55
  • 936

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

  • 2008年12月27日 20:37
  • 261KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用
举报原因:
原因补充:

(最多只允许输入30个字)