ThreadPoolExecutor使用示例

来源:互联网 发布:直播人气软件 编辑:程序博客网 时间:2024/06/07 18:44

通常,使用Java开发并发程序时,我们创建Runnable对象,然后创建对应的线程Thead对象来执行它们。Java 5之后,Java并发API提供了Executor框架,主要包括Executor接口,它的子接口ExecutorService,以及实现上述两个接口的ThreadPoolExecutor类。

 

这种机制使得任务的创建和任务的执行分离,使用executor,开发者只需要实现Runnable对象,把对象传递给executor即可。Executor会负责它们的执行、实例化并调度相关的线程。不止于此,通过使用线程池可以改善性能。当向Executor传递任务时,它会尝试使用已池化的线程来执行此任务,避免不断地创建新线程。

本文只介绍一个最基本的threadpoolexecutor的例子,更复杂任务将在后续帖子中介绍。

 

1. 创建待执行的任务

 

Java代码  收藏代码
  1. class Task implements Runnable   
  2. {  
  3.     private String name;  
  4.    
  5.     public Task(String name)   
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.        
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.    
  14.     @Override  
  15.     public void run()   
  16.     {  
  17.         try  
  18.         {  
  19.             Long duration = (long) (Math.random() * 10);  
  20.             System.out.println("Doing a task during : " + name);  
  21.             TimeUnit.SECONDS.sleep(duration);  
  22.         }   
  23.         catch (InterruptedException e)   
  24.         {  
  25.             e.printStackTrace();  
  26.         }  
  27.     }  
  28. }  

 

 2. 使用Executors执行任务

 

现在我们只需要创建一个ThreadPoolExecutor实例,将任务传递给它的execute()方法。

  

Java代码  收藏代码
  1. package com.howtodoinjava.demo.multithreading;  
  2.    
  3. import java.util.concurrent.Executors;  
  4. import java.util.concurrent.ThreadPoolExecutor;  
  5. import java.util.concurrent.TimeUnit;  
  6.    
  7. public class BasicThreadPoolExecutorExample   
  8. {  
  9.     public static void main(String[] args)   
  10.     {  
  11.         //Use the executor created by the newCachedThreadPool() method   
  12.         //only when you have a reasonable number of threads   
  13.         //or when they have a short duration.  
  14.         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();  
  15.         for (int i = 0; i <= 5; i++)   
  16.         {  
  17.             Task task = new Task("Task " + i);  
  18.             System.out.println("A new task has been added : " + task.getName());  
  19.             executor.execute(task);  
  20.         }  
  21.         executor.shutdown();  
  22.     }  
  23. }  

 输出:

 

Java代码  收藏代码
  1. new task has been added : Task 0  
  2. new task has been added : Task 1  
  3. new task has been added : Task 2  
  4. new task has been added : Task 3  
  5. new task has been added : Task 4  
  6. new task has been added : Task 5  
  7. Doing a task during : Task 5  
  8. Doing a task during : Task 0  
  9. Doing a task during : Task 2  
  10. Doing a task during : Task 1  
  11. Doing a task during : Task 4  
  12. Doing a task during : Task 3  

 一些关键点:

1. ThreadProolExecutor有四个不同的构造器,考虑到其复杂性,Java并发API提供了Executors类来构建执行器和相关对象。尽管我们可以直接通过其构造函数来生成ThreadPoolExecutor,使用Executors类是更加推荐的方式。

 

2. 上述例子中中我们创建了一个缓存线程池(cached thread pool),当执行新任务时如果需要则创建新线程;当已有线程完成了它们执行的任务之后可以重用。缓存线程池的一个缺点在于它会不断创建新的线程,如果向此executor发送了过多任务,可能会导致系统负载过高。通过固定线程线程池可以解决这个问题,后面详述

 

3. 关于ThreadPoolExecutor类的重要一点(也适用于其他的executor),你必须显式地结束它。否则它将会一直执行下去,程序将不会结束。当executor没有任务执行时,它持续等待新的任务,并不会结束执行。Java应用程序只有当所有的非守护线程都结束后才会停止执行。所以如果不结束executor,你的程序将永远不会结束。

 

4. 为了结束executor,你可以使用ThreadPoolExecutor类的shutdown()方法。当executor结束所有的待执行任务时,它将会停止运行。当调用shutdown()方法之后,如果尝试把另外一个任务发送给此执行器,执行器将会拒绝并抛出RejectedExecutionException异常。

 

5. ThreadPoolExecutor类提供了用于获取状态信息的一组方法,在示例程序中,我们可以使用getPoolSize(),getActiveCount()以及getCompletedTaskCount()方法以获取线程池大小,线程数量,此执行器执行完成的任务数量等信息。你也可以调用 getLargestPoolSize()方法获取此线程池曾经存在的最大线程数量。

 

6. ThreadPoolExecutor类也提供用于结束execuor执行的其他相关方法:

  • shutdownNow():此方法会将execuor立刻关闭。挂起的任务将不会执行。它返回所有挂起任务的列表。正在执行呃任务将继续执行,但此方法不会等待它们执行完成。
  • isTerminated():当shutdown()shutdownNow()方法被调用,并且executor结束关闭过程后,此方法返回true。
  • isShutdown():当你调用shutdown()方法之后,此方法返回true。
  • awaitTermination(long timeout, TimeUnit unit):此方法阻塞所有的调用线程,直到此执行器任务结束或超时时间到。TimeUnit类包含了如下枚举值:DAYS,HOURS,MICROSECONDS等。
原创粉丝点击