线程执行器(Thread Executors)
1、创建线程执行器
2、创建定长线程执行器
3、在返回结果的线程执行器中执行任务
4、运行多任务并且处理第一个返回结果
5、运行多任务并且处理所有返回结果
6、在一定延时后执行器运行任务
7、执行器中周期性的运行一个任务
8、取消线程执行器中的一个任务
9、控制执行器中已经完成的任务
10、分离执行器中的任务的开启,并分别处理结果。
11、控制被执行器拒绝的任务。
这本书的最大特点就是通过代码感知知识点。开启本章的内容。
通常情况下,我们可以通过运行多个thread线程开启多线程环境下的编程任务。但是在需要开启成百上千任务的环境下,这种多线程编程模式显然会成为瓶颈。
从java5开始,jdk就提供了并发编程框架(Eexcutor framwork) ,executor框架中,只需把任务分配给executor,executor负责初始化、运行线程。另一个好处是executor框架里面提供了callable接口。Callable接口有以下两个好处。
1、接口的主方法调用call()可以获得返回结果。
2、采用callable接口传递任务给executor,我们能够获得一个实现了Future接口的的对象。我们能够通过使用这个对象控制状态和返回结果。
一、创建线程执行器
package jav7concurrencycookbook.fouthpassage;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Task implements Runnable {
private Date initDate;
private String name;
public Task(String name){
this.name=name;
initDate=new Date();
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.printf("%s: Task $s created on %s",Thread.currentThread().getName(),name,this.initDate);
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 jav7concurrencycookbook.fouthpassage;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public Server(){
//Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available, and uses the provided ThreadFactory to create new threads when needed
executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();
}
public void executeTask(Task task){
System.out.printf("Server: A new task has arrived\n");
executor.execute(task);//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();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Server server=new Server();
for (int i=0; i<5; i++){
Task task=new Task("Task "+i);
server.executeTask(task);
}
server.endServer();//注意任务执行完毕关闭线程池(ThreadPoolExecutor)
}
}
二、创建定长线程执行器
executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(5);//线程池的大小为5
newSingleThreadExecutor()//线程池的大小为1,每次只能执行一个任务
三、在返回结果的线程执行器中执行任务
Executor framwork的好处之一就是你能够同时运行任务并获得结果。Java 并发API提供了以下两个接口:
1 Callable:该接口有一个call()方法。call()方法可以实现任务逻辑。同时,Callable接口是一个有个参数接口,有我们指定返回的值类型。
2、Future:该接口有一些方法,获取Callable对象的返回结果,并且管理Callable的状态。
This interface has some methods to obtain the result generated by a
Callable object and to manage its state.
package jav7concurrencycookbook.fouthpassage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
//实现Callable接口指定返回类型为Integer
public class FactorialCalculator implements Callable<Integer> {
private Integer number;
public FactorialCalculator(Integer number){
this.number=number;
}
@Override
public Integer call() throws Exception {
int result = 1;
if ((number==0)||(number==1)) {
result=1;
} else {
for (int i=2; i<=number; i++) {
result*=i;
TimeUnit.MILLISECONDS.sleep(20);
}
}
System.out.printf("%s: %d\n",Thread.currentThread().
getName(),result);
return result;
}
public static void main(String[] args) {
ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(10);
List<Future<Integer>> resultList=new ArrayList<Future<Integer>>();
Random random=new Random();
for (int i=0; i<10; i++){
Integer number= random.nextInt(10);
FactorialCalculator calculator=new FactorialCalculator(number);
Future<Integer> result=executor.submit(calculator);
try {
result.get(500, TimeUnit.SECONDS);//等待500秒,如果获取不到数据抛异常。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//等待500秒
resultList.add(result);
}
do {
System.out.printf("Main: Number of Completed Tasks:%d\n",executor.getCompletedTaskCount());
for (int i=0; i<resultList.size(); i++) {
Future<Integer> result=resultList.get(i);
System.out.printf("Main: Task %d: %s\n",i,result.
isDone());
}
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while (executor.getCompletedTaskCount()<resultList.size());
System.out.printf("Main: Results\n");
for (int i=0; i<resultList.size(); i++) {
Future<Integer> result=resultList.get(i);
Integer number=null;
try {
number=result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.printf("Main: Task %d: %d\n",i,number);
}
executor.shutdown();
}
}
四、运行多任务并且处理第一个返回结果
使用场景,如果有多个排序算法,你指关心哪个是最快的,那么可以使用该方法。
下面的例子是用户被两种机制验证,如果有一个机制验证通过认为该用户有效。
package jav7concurrencycookbook.fouthpassage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TaskValidator implements Callable<String> {
private UserValidator validator;
private String user;
private String password;
public TaskValidator(UserValidator validator, String user, String password) {
this.validator = validator;
this.user = user;
this.password = password;
}
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
if (!validator.validate(user, password)) {
System.out.printf("%s: The user has not been found\n",
validator.getName());
throw new Exception("Error validating user");
}
System.out.printf("%s: The user has been found\n", validator.getName());
return validator.getName();
}
public static void main(String[] args) {
String username = "test";
String password = "test";
UserValidator ldapValidator = new UserValidator("LDAP");//认证1
UserValidator dbValidator = new UserValidator("DataBase");//认证2
TaskValidator ldapTask = new TaskValidator(ldapValidator, username,password);//认证1任务
TaskValidator dbTask = new TaskValidator(dbValidator, username,
password);//认证2任务
List<TaskValidator> taskList = new ArrayList<TaskValidator>();//绑定为一个任务链
taskList.add(ldapTask);
taskList.add(dbTask);
ExecutorService executor = (ExecutorService) Executors
.newCachedThreadPool();
String result;
try {
result = executor.invokeAny(taskList);//只要有一个通过就可以
System.out.printf("Main: Result: %s\n", result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
System.out.printf("Main: End of the Execution\n");
}
}
返回的结果有四种情况:
1、两者都为true
2、第一个为true 第二个抛异常
3、第一个抛异常 第二个为true
4、两个都抛异常
五、运行多任务并且处理所有返回结果
List<Future<Result>>resultList=null;//返回的结果放置到Future<Result>链表中。
List<Task> taskList=new ArrayList<>();
for (int i=0; i<3; i++){
Task task=new Task(i);
taskList.add(task);
}
resultList=executor.invokeAll(taskList);
try {
resultList=executor.invokeAll(taskList);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.shutdown();
System.out.println("Main: Printing the results");
for (int i=0; i<resultList.size(); i++){
Future<Result> future=resultList.get(i);
try {
Result result=future.get();
System.out.println(result.getName()+": "+result.
getValue());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
为防止长时间不返回,invokeAll(Collection<? extends Callable<T>> tasks, long timeout,TimeUnit unit): 如果在规定时间内不返回,就跑错。
六、在一定延时后执行器运行任务
ScheduledThreadPoolExecutor executor=(ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);
for (int i=0; i<5; i++) {
Task task=new Task("Task "+i);
executor.schedule(task,i+1 , TimeUnit.SECONDS);
}
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit)
Description copied from interface: ScheduledExecutorService
Creates and executes a one-shot action that becomes enabled after the given delay.
七、执行器中周期性的运行一个任务
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period, TimeUnit unit)
Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay
then initialDelay+period
, then initialDelay + 2 * period
, and so on. If any execution of the task encounters an exception, subsequent executions are suppressed. Otherwise, the task will only terminate via cancellation or termination of the executor. If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.
创建和执行一个周期性的行为,使得在initalDelay时间后开始,在initialDelay+period时间后再来,initialDelay + 2 * period时间内再来。如果一个正在执行的任务报错,那么接下来的任务都会被一致。或者,任务通过被取消或者停止的形式被执行。如果任务的任何执行时间比周期时间长,那么接下来的执行将要延迟,不会并发执行。
八、取消线程执行器中的一个任务
Future<String> result=executor.submit(task);
result.cancel(true);
九、控制执行器中已经完成的任务
FutureTask类提供了一个叫做done()的方法,允许你在执行器中执行的任务完成之后继续一些执行一些代码。这个功能可以使用在线程有后续操作的场景,比如产生一个报告或者发送一个email或者释放资源。这个方法是FutureTask的内置方法,该方法无论在任务被取消或者正常完成之后都会执行。缺省情况下,这个方法是空的,我们可以通过实现FutureTask类实现该方法。
@Override
protected void done() {
if (isCancelled()) {
System.out.printf("%s: Has been canceled\n",name);
} else {
System.out.printf("%s: Has finished\n",name);
}
}
public ResultTask(Callable<String> callable) {
super(callable);
this.name=((ExecutableTask)callable).getName();
}
首先创建实现callnable接口的类,然后创建实现FutureTask<String>接口的类。将第一个类放置到第二个类中。并在第二个类覆盖done方法去实现done逻辑(写日志,发送email等)。
十、分离执行器中的任务的开启,并分别处理结果。
Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException
CompletionService
实现了生产者和消费者的异步解耦。生产者提交任务,消费者按照任务完成的顺序获取任务并处理结果。CompletionService
可以实现异步I/O.
poll(): The version of the poll() method without arguments checks if there are any Future objects in the queue. If the queue is empty, it returns null immediately.
Otherwise, it returns its first element and removes it from the queue. off take(): This method, without arguments, checks if there are any Future objects in
the queue. If it is empty, it blocks the thread until the queue has an element. When the queue has elements, it returns and deletes its first element from the queue.
Retrieves and removes the Future representing the next completed task, waiting if necessary up to the specified wait time if none are yet present.
package jav7concurrencycookbook.fouthpassage;
import java.util.concurrent.CompletionService;
public class ReportRequest implements Runnable {
private String name;
private CompletionService<String> service;
public ReportRequest(String name, CompletionService<String>service){
this.name=name;
this.service=service;
}
@Override
public void run() {
// TODO Auto-generated method stub
ReportGenerator reportGenerator=new ReportGenerator(name,"Report");
service.submit(reportGenerator);//放置资源
}
}
package jav7concurrencycookbook.fouthpassage;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class ReportProcessor implements Runnable {
private CompletionService<String> service;
private boolean end;
public ReportProcessor(CompletionService<String> service) {
this.service = service;
end = false;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (!end) {
Future<String> result = null;//结果
try {
result = service.poll(20, TimeUnit.SECONDS);//从队列里面获取数据
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (result != null) {//如果获取到了
String report;
try {
report = result.get();
System.out.printf("ReportReceiver:%s\n",
report);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.printf("ReportSender: End\n");
}
}
public void setEnd(boolean end) {
this.end = end;
}
}
package jav7concurrencycookbook.fouthpassage;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
/**
*
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService executor=(ExecutorService)Executors.
newCachedThreadPool();
CompletionService<String> service=new ExecutorCompletionService<String>(executor);
ReportRequest faceRequest=new ReportRequest("Face", service);//放置到队列里面
ReportRequest onlineRequest=new ReportRequest("Online", service);//放置到队列里面
Thread faceThread=new Thread(faceRequest);
Thread onlineThread=new Thread(onlineRequest);
ReportProcessor processor=new ReportProcessor(service);//获取
Thread senderThread=new Thread(processor);
System.out.printf("Main: Starting the Threads\n");
faceThread.start();
onlineThread.start();
senderThread.start();
try {
System.out.printf("Main: Waiting for the report generators.\n");
faceThread.join();
onlineThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Shutting down the executor.\n");
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
processor.setEnd(true);
System.out.println("Main: Ends");
}
}
十一、控制被执行器拒绝的任务。
public class RejectedTaskController implements RejectedExecutionHandler
ThreadPoolExecutor executor=(ThreadPoolExecutor) Executors.newCachedThreadPool();
executor.setRejectedExecutionHandler(controller);
被拒绝的任务可以通过另外配置RejectedExecutionHandler实用自己的拒绝机制处理。