这套机制分离了任务的创建和执行。通过使用执行器,仅仅需要实现Runnable接口的对象,然后将这些对象发送给执行器即可。执行器通过创建所需线程,来负责这些Runnable对象的创建,实例化以及运行。但是执行器的功能不限于此,他使用了线程池来提高应用程序的性能。当发送一个任务给执行器时,执行器会尝试使用线程池中的线程来执行这个任务,避免了不断地创建和销毁线程而导致的性能下降。
创建线程执行器
使用执行器框架(Executor Framework)第一步是创建ThreadPoolExecutor对象。可以ThreadPoolExecutor类提供的四个构造器或者使用Executors的工厂类来创建ThreadPoolExecutor对象。
package com.packtpub.java7.concurrency.chapter4.recipe1.task;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* This class implements a concurrent task
*
*/
public class Task implements Runnable {
/**
* The start date of the task
*/
private Date initDate;
/**
* The name of the task
*/
private String name;
/**
* Constructor of the class. Initializes the name of the task
* @param name name asigned to the task
*/
public Task(String name){
initDate=new Date();
this.name=name;
}
/**
* This method implements the execution of the task. Waits a random period of time and finish
*/
@Override
public void run() {
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());
}
}
package com.packtpub.java7.concurrency.chapter4.recipe1.task;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* This class simulates a server, for example, a web server, that receives
* requests and uses a ThreadPoolExecutor to execute those requests
*
*/
public class Server {
/**
* ThreadPoolExecutors to manage the execution of the request
*/
private ThreadPoolExecutor executor;
/**
* Constructor of the class. Creates the executor object
*/
public Server(){
executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();
}
/**
* This method is called when a request to the server is made. The
* server uses the executor to execute the request that it receives
* @param task The request made to the server
*/
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());
}
/**
* This method shuts down the executor
*/
public void endServer() {
executor.shutdown();
}
}
package com.packtpub.java7.concurrency.chapter4.recipe1.core;
import com.packtpub.java7.concurrency.chapter4.recipe1.task.Server;
import com.packtpub.java7.concurrency.chapter4.recipe1.task.Task;
/**
* Main class of the example. Creates a server and 100 request of the Task class
* that sends to the server
*
*/
public class Main {
/**
* Main method of the example
* @param args
*/
public static void main(String[] args) {
// Create the server
Server server=new Server();
// Send 100 request to the server and finish
for (int i=0; i<100; i++){
Task task=new Task("Task "+i);
server.executeTask(task);
}
server.endServer();
}
}
如果需要执行新的任务,缓存线程池就会创建新线程;如果线程所运行的任务执行完成后并且这个线程可用,那么缓存线程池将会重用这些线程。线程的重用的优点是减少了创建新线程所花费的时间。然而,新任务固定会依赖线程来执行,因此缓存线程池也有缺点,如果发送过多的任务给执行器,系统的负荷将会过载。
PS:仅当线程的数量是合理的或者线程只会运行很短的时间,合适采用Executors工厂类的newCachedThreadPool()方法来创建执行器。
ThreadPoolExecutor类的重要特性是,通常需要显示地去结束他。