并发编程Future and Callable
目录:
1、Future及Callable基本介绍:
2、主要方法介绍:
3、Callable与Runnable的主要区别:
4、示例代码及运行结果:
1、Future及Callable基本介绍:
Future接口:
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。
继承关系:
java.util.concurrent.Future<V>:V——get方法所返回的结果类型
使用原因:
获得Callable接口产生的返回值,以便进行后期的处理。
Callable接口:
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
继承关系:
java.util.concurrent.Callable<V>:V——call方法的结果类型
使用原因:
使得线程可以产生返回值。
2、主要方法介绍:
Future接口:
boolean cancel(boolean mayInterruptIfRunning):如果线程正在运行则是否中断正在运行的线程,在代码中需要使用if(Thread.currentThread().isInterrupted())进行配合,结果返回true表示成功发送取消任务的命令,无阻塞特性;
V get():用于获得Callable的返回值,具有阻塞特性;
V get(long timeout TimeUnit unit):在指定的最大时间内等待获得返回值,超过最大等待时间,则抛出TimeoutException异常,具有阻塞特性;
boolean isCancelled():如果在任务正常完成前将其取消,则返回true,无阻塞特性;
boolean isDone():如果任务已完成,则返回true,无阻塞特性;
Callable接口:
V call():计算结果,如果无法计算结果,则抛出一个异常;
ExecutorService接口:
void shutdown():执行以前提交的任务,但不再接受提交的新任务。通常shutdown后调用awaitTermination,awaitTermination会阻塞当前线程,等待以前提交的任务执行完,然后才继续向下执行;
<T> Future<T> submit(Callable<T> task):提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future,说明submit()方法支持有返回值的功能;
<T> Future<T> submit(Callable<T> task, T result):第二个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得;
boolean awaitTermination(long timeout, TimeUnit unit):当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false,具有阻塞特性;
Future<?> submit(Runnable task):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future,说明submit()方法支持无返回值的功能;
3、Callable与Runnable的主要区别:
(1)Callable接口的call()方法可以有返回值,而Runnable接口的run()方法没有返回值;
(2)Callable接口的call()方法就可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常;
(3)执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。
4、示例代码及运行结果:
(1)工程目录图:
图4.1 工程目录图
(2)FutureAndCallableService.java
package com.remoa.futureAndCallable.service;
import java.text.SimpleDateFormat;
public interface FutureAndCallableService {
SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
void Test();
}
(3)Test1MyCallable1.java
package com.remoa.futureAndCallable.domain.callable;
import com.remoa.futureAndCallable.domain.Remoa;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Callable;
public class Test1MyCallable1 implements Callable<List<Remoa>> {
@Override
public List<Remoa> call() throws Exception {
Thread.sleep(500);
List<Remoa> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 5; i++) {
String uuid = UUID.randomUUID().toString().substring(0,10);
Remoa remoa = new Remoa();
remoa.setAge(random.nextInt(29) + 1);
remoa.setName("Remoa" + i);
remoa.setStudentId(uuid);
list.add(remoa);
}
return list;
}
}
(4)TestMyCallable2.java
package com.remoa.futureAndCallable.domain.callable;
import com.remoa.futureAndCallable.domain.Remoa;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
public class Test1MyCallable2 implements Callable<List<Remoa>> {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public List<Remoa> call() throws Exception {
Thread.sleep(500);
List<Remoa> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 5; i++) {
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName() + "中断时间为:" + simpleDateFormat.format(new Date()));
throw new InterruptedException();
}
String uuid = UUID.randomUUID().toString().substring(0,10);
Remoa remoa = new Remoa();
remoa.setAge(random.nextInt(29) + 1);
remoa.setName("Remoa" + i);
remoa.setStudentId(uuid);
list.add(remoa);
}
return list;
}
}
(5)Test1ServcieImp.javapackage com.remoa.futureAndCallable.service.Imp;
import com.remoa.futureAndCallable.domain.callable.Test1MyCallable1;
import com.remoa.futureAndCallable.domain.callable.Test1MyCallable2;
import com.remoa.futureAndCallable.domain.Remoa;
import com.remoa.futureAndCallable.service.FutureAndCallableService;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* cancel中断线程方法的测试
*/
public class Test1ServiceImp implements FutureAndCallableService {
public Test1ServiceImp(){
String date = SIMPLE_DATE_FORMAT.format(new Date());
System.out.println(date + "开始进行cancel中断线程方法的测试");
}
@Override
public void Test() {
Test1MyCallable1 test1MyCallable1 = new Test1MyCallable1();
Test1MyCallable2 test1MyCallable2 = new Test1MyCallable2();
/*
int corePoolSize:池中保存的线程数,包括空闲线程
int maximumPoolSize:池中允许的最大线程数
long keepAliveTime:当线程数大于池中保存的线程数时,此为终止多余的空闲线程等待新任务的最长时间
TimeUnit unit:keepAliveTime参数的时间单位
BlockingQueue<Runnable> workQueue:执行前用于保持任务的队列,此队列仅保持由execute方法提交的Runnable任务
*/
ExecutorService executorService = new ThreadPoolExecutor(10, 20, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
try {
Future<List<Remoa>> future1 = executorService.submit(test1MyCallable1);
System.out.println("(1)先执行具有阻塞特性的get方法:");
for (int i = 0; i < future1.get().size(); i++) {
System.out.println(future1.get().get(i));
}
System.out.println("成功发送任务取消的命令了吗?" + future1.cancel(true));
System.out.println("任务是在正常完成前将其取消的吗?" + future1.isCancelled());
} catch (InterruptedException e) {
System.out.println("Future1抛出了中断异常");
} catch (ExecutionException e) {
System.out.println("Future1抛出了执行异常");
}finally {
try {
Thread.sleep(1000);
System.out.println("--------------------");
Future<List<Remoa>> future2 = executorService.submit(test1MyCallable1);
System.out.println("(2)先执行发送任务取消的命令:");
System.out.println("成功发送任务取消的命令了吗?" + future2.cancel(true));
System.out.println("任务是在正常完成前将其取消的吗?" + future2.isCancelled());
System.out.println("后执行具有阻塞特性的get方法:");
System.out.println(future2.get().size());
for (int i = 0; i < future2.get().size(); i++) {
System.out.println(future2.get().get(i));
}
} catch (InterruptedException e) {
System.out.println("Future2抛出了中断异常");
} catch (ExecutionException e) {
System.out.println("Future2抛出了执行异常");
} catch(CancellationException e){
System.out.println("Future2抛出了取消任务异常");
}finally{
try {
Thread.sleep(1000);
System.out.println("--------------------");
Future<List<Remoa>> future3 = executorService.submit(test1MyCallable2);
System.out.println("(3)配合isInterrupted一起使用");
System.out.println("先执行发送任务取消的命令,cancel方法参数选择为false:");
//发送取消的命令,发送成功返回true,但是方法参数传的是false,则线程不会中断会一直执行
System.out.println("成功发送任务取消的命令了吗?" + future3.cancel(false));
System.out.println("任务是在正常完成前将其取消的吗?" + future3.isCancelled());
System.out.println("后执行具有阻塞特性的get方法:");
for (int i = 0; i < future3.get().size(); i++) {
System.out.println(future3.get().get(i));
}
} catch (InterruptedException e) {
System.out.println("Future3抛出了中断异常");
} catch (ExecutionException e) {
System.out.println("Future3抛出了执行异常");
} catch(CancellationException e){
System.out.println("Future3抛出了取消任务异常");
}finally {
try {
Thread.sleep(1000);
System.out.println("--------------------");
Future<List<Remoa>> future4 = executorService.submit(test1MyCallable2);
System.out.println("(4)配合isInterrupted一起使用");
System.out.println("先执行发送任务取消的命令,cancel方法参数选择为true:");
//设置了sleep睡眠时间,则线程已经跑完,中断命令不再起作用,将返回false
Thread.sleep(800);
System.out.println("成功发送任务取消的命令了吗?" + future4.cancel(true));
System.out.println("任务是在正常完成前将其取消的吗?" + future4.isCancelled());
System.out.println("后执行具有阻塞特性的get方法:");
for (int i = 0; i < future4.get().size(); i++) {
System.out.println(future4.get().get(i));
}
} catch (InterruptedException e) {
System.out.println("Future4抛出了中断异常");
} catch (ExecutionException e) {
System.out.println("Future4抛出了执行异常");
}
}
}
}
}
}
运行结果:
图4.2 测试1截图
(6)Test2MyCallable.java
package com.remoa.futureAndCallable.domain.callable;
import com.remoa.futureAndCallable.domain.Remoa;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
public class Test2MyCallable implements Callable<Remoa>{
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Remoa call() throws Exception {
System.out.println(simpleDateFormat.format(new Date()));
Thread.sleep(10000);
Remoa remoa = new Remoa();
remoa.setName("Remoa");
remoa.setAge(21);
remoa.setStudentId("3114005847");
return remoa;
}
}
(7)Test2ServiceImp.java
package com.remoa.futureAndCallable.service.Imp;
import com.remoa.futureAndCallable.domain.Remoa;
import com.remoa.futureAndCallable.domain.callable.Test2MyCallable;
import com.remoa.futureAndCallable.service.FutureAndCallableService;
import java.util.Date;
import java.util.concurrent.*;
/**
* Future.get(long timeout, TimeUnit unit)方法的测试
*/
public class Test2ServiceImp implements FutureAndCallableService {
public Test2ServiceImp(){
String date = SIMPLE_DATE_FORMAT.format(new Date());
System.out.println(date + "开始进行get(long timeout, TimeUnit unit)方法的测试");
}
@Override
public void Test() {
Test2MyCallable myCallable = new Test2MyCallable();
ExecutorService executorService = new ThreadPoolExecutor(10, 20, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
Future<Remoa> future = executorService.submit(myCallable);
System.out.println("设置在五秒中内等待获得返回值:");
try {
System.out.println(future.get(5,TimeUnit.SECONDS));
} catch (InterruptedException e) {
System.out.println("Future抛出了线程中断异常");
} catch (ExecutionException e) {
System.out.println("Future抛出了执行异常");
} catch (TimeoutException e) {
System.out.println("Future抛出了超时异常");
}
}
}
运行结果:
图4.3 测试2截图
(8)Test3MyCallable.java
package com.remoa.futureAndCallable.domain.callable;
import com.remoa.futureAndCallable.domain.Remoa;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
public class Test3MyCallable implements Callable<Remoa> {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String username;
private String studentId;
private int age;
public Test3MyCallable(String username, String studentId, int age){
this.username = username;
this.studentId = studentId;
this.age = age;
}
@Override
public Remoa call() throws Exception {
System.out.println(simpleDateFormat.format(new Date()));
Remoa remoa = new Remoa();
remoa.setName(username);
remoa.setAge(age);
remoa.setStudentId(studentId);
return remoa;
}
}
(9)Test3ServiceImp.java
package com.remoa.futureAndCallable.service.Imp;
import com.remoa.futureAndCallable.domain.Remoa;
import com.remoa.futureAndCallable.domain.callable.Test3MyCallable;
import com.remoa.futureAndCallable.service.FutureAndCallableService;
import com.remoa.futureAndCallable.service.MyRejectedExceptionHandler;
import java.util.Date;
import java.util.concurrent.*;
/**
* 自定义拒绝策略RejectedExecutionHandler接口使用的测试
*/
public class Test3ServiceImp implements FutureAndCallableService {
public Test3ServiceImp(){
String date = SIMPLE_DATE_FORMAT.format(new Date());
System.out.println(date + "开始进行自定义拒绝策略RejectedExecutionHandler接口使用的测试");
}
@Override
public void Test() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
executor.setRejectedExecutionHandler(new MyRejectedExceptionHandler());
ExecutorService executorService = executor;
Future<Remoa> future1 = executorService.submit(new Test3MyCallable("Remoa1", "3114005847", 21));
Future<Remoa> future2 = executorService.submit(new Test3MyCallable("Remoa2", "3114005848", 22));
Future<Remoa> future3 = executorService.submit(new Test3MyCallable("Remoa3", "3114005849", 23));
try {
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
} catch (InterruptedException e) {
System.out.println("Future1或Future2或Future3发生了中断异常");
} catch (ExecutionException e) {
System.out.println("Future1或Future2或Future3发生了执行异常");
}
executor.shutdown();
Future<Remoa> future4 = executorService.submit(new Test3MyCallable("Remoa4", "3114005850", 24));
try {
System.out.println(future4.get());
} catch (InterruptedException e) {
System.out.println("Future4发生了中断异常");
} catch (ExecutionException e) {
System.out.println("Future4发生了执行异常");
}
}
}
(10)MyRejectedExceptionHandler.java
package com.remoa.futureAndCallable.service;
import com.remoa.futureAndCallable.domain.Menu;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* RejectedExecutionHandler其主要作用是当线程池关闭后依然后任务要执行时,可以实现一些处理。
*/
public class MyRejectedExceptionHandler implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(((FutureTask)r).toString() + "被拒绝");
Menu.getMenu();
}
}
运行结果:(11)Test4MyCallable.java
package com.remoa.futureAndCallable.domain.callable;
import com.remoa.futureAndCallable.domain.Remoa;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
public class Test4MyCallable implements Callable<List<Remoa>>{
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public List<Remoa> call() throws Exception {
System.out.println(Thread.currentThread().getName() + ",开始执行的时间为:" + simpleDateFormat.format(new Date()));
List<Remoa> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < (random.nextInt(1000) + 10) * 100000; i++) {
String uuid = UUID.randomUUID().toString().substring(0,10);
Remoa remoa = new Remoa();
remoa.setAge(random.nextInt(29) + 1);
remoa.setName("Remoa" + i);
remoa.setStudentId(uuid);
list.add(remoa);
}
System.out.println(Thread.currentThread().getName() + ", list's size = " + list.size());
System.out.println(Thread.currentThread().getName() + "任务结束,结束时间为:" + simpleDateFormat.format(new Date()));
return list;
}
}
(12)Test4ServiceImp.java
package com.remoa.futureAndCallable.service.Imp;
import com.remoa.futureAndCallable.domain.callable.Test4MyCallable;
import com.remoa.futureAndCallable.service.FutureAndCallableService;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* ExecutorService的相关方法测试
*/
public class Test4ServiceImp implements FutureAndCallableService {
public Test4ServiceImp(){
String date = SIMPLE_DATE_FORMAT.format(new Date());
System.out.println(date + "开始进行ExecutorService的相关方法的测试");
}
@Override
public void Test() {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Test4MyCallable callable = new Test4MyCallable();
try {
while(true) {
executorService.submit(callable);
executorService.submit(callable);
executorService.submit(callable);
executorService.submit(callable);
executorService.submit(callable);
break;
}
executorService.shutdown();
boolean flag = executorService.awaitTermination(5, TimeUnit.HOURS);
if(flag == false){
executorService.shutdownNow();
System.out.println("任务超时,未完成!");
}
System.out.println("哈哈哈,执行最后的语句了");
} catch (InterruptedException e) {
System.out.println("发生了中断异常。");
}
}
}
运行结果:
图4.5 测试4截图
(13)Menu.java
package com.remoa.futureAndCallable.domain;
import com.remoa.futureAndCallable.Strategy.ChooserTest;
import java.util.Scanner;
/**
* 菜单类
*/
public class Menu {
public static void getMenu(){
String choiceStr = "";
Scanner scanner = new Scanner(System.in);
ChooserTest chooserTest = new ChooserTest();
while(!choiceStr.equals("0")) {
System.out.println("-------------测试选项------------------------------------");
System.out.println("| 0、退出 |");
System.out.println("| 1、测试cancel中断线程方法 |");
System.out.println("| 2、测试get(long timeout, TimeUnit unit)方法 |");
System.out.println("| 3、测试自定义拒绝策略RejectedExecutionHandler接口的使用 |");
System.out.println("| 4、测试ExecutorService的相关方法 |");
System.out.println("--------------------------------------------------------");
System.out.print("请输入您的选择:");
choiceStr = scanner.nextLine();
chooserTest.chooseTestDemo(choiceStr);
}
}
}
运行结果:
图4.6 菜单选项截图
(14)Remoa.java
package com.remoa.futureAndCallable.domain;
/**
* Remoa实体类
*/
public class Remoa {
private String name;
private String studentId;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Remoa{" +
"name='" + name + '\'' +
", studentId='" + studentId + '\'' +
", age=" + age +
'}';
}
}
(15)ChooserTest.javapackage com.remoa.futureAndCallable.Strategy;
import com.remoa.futureAndCallable.service.*;
import com.remoa.futureAndCallable.service.Imp.Test1ServiceImp;
import com.remoa.futureAndCallable.service.Imp.Test2ServiceImp;
import com.remoa.futureAndCallable.service.Imp.Test3ServiceImp;
import com.remoa.futureAndCallable.service.Imp.Test4ServiceImp;
/**
* 测试方法选择器
*/
public class ChooserTest {
FutureAndCallableService futureAndCallableService = null;
public void chooseTestDemo(String chooseStr){
switch(chooseStr){
case "0":
System.exit(0);
break;
case "1":
futureAndCallableService = new Test1ServiceImp();
break;
case "2":
futureAndCallableService = new Test2ServiceImp();
break;
case "3":
futureAndCallableService = new Test3ServiceImp();
break;
case "4":
futureAndCallableService = new Test4ServiceImp();
break;
default:
System.out.println("You offer an invalid number of choices!Please try again or input '0' to exit");
break;
}
futureAndCallableService.Test();
}
}
(16)FutureAndCallableAction.java
package com.remoa.futureAndCallable.action;
import com.remoa.futureAndCallable.domain.Menu;
/**
* 测试入口类
*/
public class FutureAndCallableAction {
public static void main(String[] args) {
Menu.getMenu();
}
}