使用 Executor 框架的第一步是创建一个 ThreadPoolExecutor 类对象。 您可使用此类提供的4个构造函数或使用一个名为Executors的工厂类。 一旦您有了一个 executor,您可将 Runnable 或 Callable 对象发送给它去执行。
线程类 : Task
private Date initDate; private String name; public Task(String name) { initDate = new Date(); this.name = name; }
System.out.printf("%s: Task %s: Created on: %s\n", Thread.currentThread().getName(), name, initDate); System.out.printf("%s: Task %s: Started on: %s\n", Thread.currentThread().getName(), name, new Date()); try { Long duration = (long) (Math.random() * 10); System.out.printf("%s: Task %s: Doing a task during %d seconds\n", Thread.currentThread().getName(), name, duration); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("%s: Task %s: Finished on: %s\n",Thread.currentThread().getName(),name,new Date());
线程类 : Server : 使用一个执行者来执行每一个任务
private ThreadPoolExecutor executor; public Server() { executor = (ThreadPoolExecutor)Executors.newCachedThreadPool(); }
public void executeTask(Task task) { System.out.printf("Server: A new task has arrived\n"); executor.execute(task); System.out.printf("Server: Pool Size: %d\n",executor.getPoolSize()); System.out.printf("Server: Active Count: %d\n",executor.getActiveCount()); System.out.printf("Server: Completed Tasks: %d\n",executor.getCompletedTaskCount()); }
public void endServer() { executor.shutdown(); }
控制类 : Main
Server server = new Server(); for (int i = 0; i < 100; i++) { Task task = new Task("Task " + i); server.executeTask(task); } server.endServer();
本例的关键是 Server 类.此类创建并使用 ThreadPoolExecutor 来执行任务.
第一个重要的地方是在 Server 类的构造函数中 ThreadPoolExecutor 的创建。 ThreadPoolExecutor 类有4个不同的构造函数,但是因为它们的复杂性,Java concurrency API提供了 Executors 类来构造执行者及其他相关对象. 尽管能直接使用其中一个构造函数来创建 ThreadPoolExecutor,仍推荐使用 Executors 类.
在本例中,使用 newCachedThreadPool() 方法创建了一个线程池。此方法返回一个 ExecutorService 对象,能被转为 ThreadPoolExecutor 以执行它所有的方法。 创建的线程缓存池在需要时创建新的线程来执行新的任务,并重用已有的执行完任务、现在可用的线程。 线程的回收利用的好处是减少了线程创建的时间;不好的地方是总是有那么多线程准备执行新的任务。 如果发送过多的任务到 executor,系统可能过载。
一旦创建了 executor,就可用 execute() 方法发送 Runnable 或 Callable 类型的任务来执行。 (本例中发送的是实现 Runnable 接口的 Task 类对象)
您已输出了一些带有 executor 相关信息的日志信息。具体地说,您使用了以下方法:
ThreadPoolExecutor 类的一个重要的方面,也是执行者们的一个共同点,就是必须明确地结束它。 如果您不这么做,执行者将继续执行,程序不会结束。如果执行者没有任务执行,它继续等待新任务且不会结束。 一个 Java 应用不会结束直到它所有的非守护线程结束执行,所以,如果您不终止执行者,您的应用程序永远不会结束。
为了告知执行者您想结束它,可使用 ThreadPoolExecutor 类的 shutdown() 方法。 当执行者完成了所有待处理任务的执行,就结束它自己的执行。 调用 shutdown() 方法后,如果您尝试发送另一个任务给执行者,任务将被拒绝,执行者将抛出 RejectedExecutionException 异常。
ThreadPoolExecutor 类提供了很多方法来获取它的状态信息。 本例中使用了 getPoolSize()、getActiveCount() 和 getCompletedTaskCount() 方法来获得关于线程池大小、线程数目和执行者已完成任务数。 也可使用 getLargestPoolSize() 方法返回每次在线程池内的最大线程数目。
ThreadPoolExecutor 类也提供了与执行者的结束有关的其他方法。如下: