在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成。5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务。这个架构主要有三个接口和其相应的具体类组成。这三个接口是Executor, ExecutorService、ScheduledExecutorService,让我们先用一个图来显示它们的关系:
图的左侧是接口,图的右侧是这些接口的具体类。注意Executor是没有直接具体实现的。
Executor接口:是用来执行Runnable任务的,它只定义一个方法:
- execute(Runnable command):执行Ruannable类型的任务
ExecutorService接口:ExecutorService继承了Executor的方法,并提供了执行Callable任务和中止任务执行的服务,其定义的方法主要有:
- submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
- invokeAll(collection of tasks):批处理任务集合,并返回一个代表这些任务的Future对象集合
- shutdown():在完成已提交的任务后关闭服务,不再接受新任务
- shutdownNow():停止所有正在执行的任务并关闭服务。
- isTerminated():测试是否所有任务都执行完毕了。
- isShutdown():测试是否该ExecutorService已被关闭
ScheduledExecutorService接口在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:
- schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行。
- scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行
- scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行。
重要的Executors类
虽然以上提到的接口有其实现的具体类,但为了方便Java 5.0建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是Executors是一个类,不是Executor的复数 形式。Executors提供了以下一些static的方法:
- callable(Runnable task): 将Runnable的任务转化成Callable的任务
- newSingleThreadExecutor: 产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行。
- newCachedThreadPool(): 产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用。
- newFixedThreadPool(int poolSize):产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个queue里顺序执行。
- newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行。
- newScheduledThreadPool(int poolSize): 产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行
实例:
1 import java.io.*; 2 import java.util.*; 3 import java.util.concurrent.*; 4 5 public class ThreadPoolTest 6 { 7 public static void main(String[] args) throws Exception 8 { 9 Scanner in = new Scanner(System.in); 10 System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): "); 11 String directory = in.nextLine(); 12 System.out.print("Enter keyword (e.g. volatile): "); 13 String keyword = in.nextLine(); 14 15 ExecutorService pool = Executors.newCachedThreadPool(); 16 17 MatchCounter counter = new MatchCounter(new File(directory), keyword, pool); 18 Future<Integer> result = pool.submit(counter); 19 20 try 21 { 22 System.out.println(result.get() + " matching files."); 23 } 24 catch (ExecutionException e) 25 { 26 e.printStackTrace(); 27 } 28 catch (InterruptedException e) {} 29 pool.shutdown(); 30 31 int largestPoolSize = ((ThreadPoolExecutor) pool).getLargestPoolSize(); 32 System.out.println("largest pool size=" + largestPoolSize); 33 } 34 } 35 36 /** 37 This task counts the files in a directory and its subdirectories that contain a given keyword. 38 */ 39 class MatchCounter implements Callable<Integer> 40 { 41 /** 42 Constructs a MatchCounter. 43 @param directory the directory in which to start the search 44 @param keyword the keyword to look for 45 @param pool the thread pool for submitting subtasks 46 */ 47 public MatchCounter(File directory, String keyword, ExecutorService pool) 48 { 49 this.directory = directory; 50 this.keyword = keyword; 51 this.pool = pool; 52 } 53 54 public Integer call() 55 { 56 count = 0; 57 try 58 { 59 File[] files = directory.listFiles(); 60 ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>(); 61 62 for (File file : files) 63 if (file.isDirectory()) 64 { 65 MatchCounter counter = new MatchCounter(file, keyword, pool); 66 Future<Integer> result = pool.submit(counter); 67 results.add(result); 68 } 69 else 70 { 71 if (search(file)) count++; 72 } 73 74 for (Future<Integer> result : results) 75 try 76 { 77 count += result.get(); 78 } 79 catch (ExecutionException e) 80 { 81 e.printStackTrace(); 82 } 83 } 84 catch (InterruptedException e) {} 85 return count; 86 } 87 88 /** 89 Searches a file for a given keyword. 90 @param file the file to search 91 @return true if the keyword is contained in the file 92 */ 93 public boolean search(File file) 94 { 95 try 96 { 97 Scanner in = new Scanner(new FileInputStream(file)); 98 boolean found = false; 99 while (!found && in.hasNextLine()) 100 { 101 String line = in.nextLine(); 102 if (line.contains(keyword)) found = true; 103 } 104 in.close(); 105 return found; 106 } 107 catch (IOException e) 108 { 109 return false; 110 } 111 } 112 113 private File directory; 114 private String keyword; 115 private ExecutorService pool; 116 private int count; 117 }