第四章 Thread Executors(Executors多线程架构)【上】

来源:互联网 发布:知乎解除手机号绑定 编辑:程序博客网 时间:2024/06/14 12:55

本章涉及内容:

  • 创建一个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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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方法执行完获取最新的结果。