本章涉及内容:
- 创建一个executor线程
- 创建一个固定大小executor
- 执行executor任务返回一个结果
- 处理多任务和处理第一个结果
- 处理多任务和处理所有结果
- 延迟之后运行executor
- 周期地运行任务的executor
- 在executor取消任务
- 在executor控制任务完成
- 在executor分开task启动和处理结果
- 控制executor的拒绝的任务
简介:
executor是一个为了解决出现大量线程的时候,executor管理和处理线程。
1、创建一个executor线程
例子:模拟web服务接收处理多个客户端请求
package com.jack;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Task implements Runnable {
private Date initDate;
private String name;
public Task(String name){
initDate = new Date();
this.name = name;
}
@Override
public void run() {
System.out.printf("%s: Task %s:创建日期:%s\n", Thread.currentThread().getName(),
name, initDate);
System.out.printf("%s: Task %s: 启动日期:%s\n", Thread.currentThread().getName(),
name, new Date());
try{
Long duration = (long) (Math.random()*10);
System.out.printf("%s: Task %s: 正在执行任务持续 %d 秒\n", Thread.currentThread().getName(),name, duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("%s: Task %s: 完成日期: %s\n", Thread.currentThread().getName(), name, new Date());
}
}
package com.jack;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public Server(){
executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
}
public void executeTask(Task task){
System.out.printf("服务器:一个新的任务已经到达\n");
executor.execute(task);
System.out.printf("服务器:线程池的大小: %d\n", executor.getPoolSize());
System.out.printf("服务器:激活的数量:%d\n", executor.getActiveCount());
System.out.printf("服务器:完成任务的数量: %d\n", executor.getCompletedTaskCount());
}
public void endServer(){
executor.shutdown();
}
}
package com.jack;
public class Main {
public static void main(String[] args) {
Server server = new Server();
for (int i=0; i<10; i++){
Task task = new Task("Task " + i);
server.executeTask(task);
}
server.endServer();
}
}
日志:
总结:
- 1、只要实现Runnable接口的线程类,然后通过Executor进行执行(execute(task)方法)启动的线程
- 2、最后关闭Executor管理器(ThreadPoolExecutor)
2、创建固定大小的Executor
只需修改一个Server类
package com.jack;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public Server(){
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
}
public void executeTask(Task task){
System.out.printf("服务器:一个新的任务已经到达\n");
executor.execute(task);
System.out.printf("服务器:线程池的大小: %d\n", executor.getPoolSize());
System.out.printf("服务器:激活的数量:%d\n", executor.getActiveCount());
System.out.printf("服务器:完成任务的数量: %d\n", executor.getCompletedTaskCount());
System.out.printf("服务器:任务的数量: %d\n", executor.getTaskCount());
}
public void endServer(){
executor.shutdown();
}
}
修改点:
1、Executors.newFixedThreadPool(5);固定大小为5
2、executeTask(task) 增加日志 System.out.printf("服务器:任务的数量: %d\n", executor.getTaskCount());
日志:
总结:
- 1、如果只是运行一个线程呢,那么使用newSingleThreadExecutor()方法。
3、在executor任务返回一个结果
Callable: 这个接口有一个call()方法,调用这个方法返回结果
Future : 这个接口获取Callable接口生成的结果。
例子:学习斐波拉契数列
package com.jack;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class FactorialCalculator implements Callable<Integer> {
private Integer number;
public FactorialCalculator(Integer number) {
super();
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("FactorialCalculator===%s: %d %d\n", Thread.currentThread().getName(),result,number);
return result;
}
}
总结:首先实现Callable<Integer>接口(Integer表示返回结果类型)
package com.jack;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;
package com.jack;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;
public class Main {
public static void main(String[] args) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
List<Future<Integer>> resultList =new ArrayList<>();
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);
resultList.add(result);
}
do {
System.out.printf("Main: 完成的任务的数量: %d\n", executor.getCompletedTaskCount());
for (int i=0; i<resultList.size(); i++){
Future<Integer> result = resultList.get(i);
System.out.printf("Main : 任务 %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: 结果 \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: 任务 %d : %d\n", i,number);
}
executor.shutdown();
}
}
总结:
- 1、创建一个固定大小为2的线程池
- 2、创建接受结果的List<Future<Integer>> 集合
- 3、循环创建十个实现Callable接口的实例,然后用线程池的submit()方法,它将返回一个执行的结果(这个结果不会立即返回,这里只是注册)
- 4、通过判断线程池完成的数量是否等于注册的数量。结果isDone()方法也可以判断结果是否返回。
- 5、最后循环打印结果,get()方法(如果没有结果返回会一直阻塞)
4、运行多个线程处理第一个结果。
类似比赛时候,我们只关心谁第一个到达终点。或者是验证排序算法的优越性
package com.jack;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class UserValidator {
private String name;
public UserValidator(String name) {
super();
this.name = name;
}
public boolean validate(String name, String password){
Random random = new Random();
try {
long duration = (long) (Math.random()*10);
System.out.printf("验证 %s: 验证一个用户花费 %d 秒\n", this.name, duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
return false;
}
return random.nextBoolean();
}
public String getName() {
return name;
}
}
总结:
1、创建一个验证用户的类,随机验证
package com.jack;
import java.util.concurrent.Callable;
public class TaskValidator implements Callable<String>{
private UserValidator validator;
private String user;
private String password;
public TaskValidator(UserValidator validator, String user, String password) {
super();
this.validator = validator;
this.user = user;
this.password = password;
}
@Override
public String call() throws Exception {
if(!validator.validate(user, password)){
System.out.printf("%s: 这个用户没有找到\n", validator.getName());
throw new Exception ("用户验证失败");
}
System.out.printf("%s: 用户已经找到\n", validator.getName());
return validator.getName();
}
}
package com.jack;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
String username = "test";
String password = "test";
UserValidator ldapValidator = new UserValidator("LDAP");
UserValidator dbValidator = new UserValidator("dataBase");
TaskValidator ldapTask = new TaskValidator(ldapValidator, username, password);
TaskValidator dbTask = new TaskValidator(dbValidator, username, password);
List<TaskValidator> taskList =new ArrayList<>();
taskList.add(ldapTask);
taskList.add(dbTask);
ExecutorService executor = Executors.newCachedThreadPool();
String result;
try {
result = executor.invokeAny(taskList);
System.out.printf("Main : 结果: %s\n", result);
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
executor.shutdown();
System.out.printf("Main : Execution执行结束\n");
}
}
- 1、创建用户验证。
- 2、创建一个newCachedThreadPool() 返回一个ExecutorService.
- 3、executor.invokeAny(taskList) 表示只要其中有个call方法执行完获取最新的结果。