- 1、Future 异步执行获取执行结果
我们知道通过Thread+Runnable可异步执行某项操作,但是如何异步执行后,并获取执行结果?
Thread和Runnable并没有提供获取执行结果的操作。
Runnable 是对任务的封装。
在JDK1.5提供了Future和Callable 接口的定义,
Future是对异步执行线程并获取结果的线程封装,提供获取执行结果或取消任务操作。
Callable<T> 接口是完成异步执行任务的封装,T call()方法执行业务逻辑。
要想异步执行任务,并获取结果,就必须同时实现Runnable和Future 2个接口,JDK提供了FutureTask<T> implements RunnableFuture<V> 类型。
获取异步执行并获取结果逻辑:
1、自定义业务实现 Callable<T> 接口,并实现call()
2、创建FutureTask<T> 实例,传递Callable实例
FutureTask<Integer> futureTask=new FutureTask<>(callable)
3、通过线程启动任务
new Thead(futureTask).start()
4、如果想获取结果,堵塞(内部是通过堵塞或轮询实现)
T t=futureTask.get();
5、如果想取消任务,是通过信号标识取消的,需要在call()中实现,通过信号量取消逻辑
futureTask.cancel(true);
- 2、Future 异步执行,获取执行结果案例
public class FutureTest {
//业务类(执行方法获取返回值)
private static class BussInfo implements Callable<Integer> {
private Integer count;
public BussInfo(Integer i){
count=i;
}
//具体业务是对值加100
@Override
public Integer call() throws Exception {
//判断任务是否取消
while(count<50000) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("==================任务已取消================");
break;
} else {
count++;
}
}
return count;
}
}
public static void main(String[] args) {
//线程异步执行,获取结果
BussInfo bussInfo=new BussInfo(10);
FutureTask<Integer> futureTask=new FutureTask<>(bussInfo);
new Thread(futureTask).start();
//模拟任务执行,有时可取消任务
Random rd=new Random();
int i = rd.nextInt(10);
try {
//线程会堵塞
Integer integer=0;
if(i>5) {
integer = futureTask.get();
}else{
//取消任务,使用的是信号,需要在call()写信号判断
futureTask.cancel(true);
}
System.out.println("integer = " + integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-main is ended");
}
}
3、采用Future实现异步的缺点,采用ForkJoin 思想实现的ForkJoinPool完成任务拆分和合并案例
1、获取结果需要堵塞get()或轮询isDone(),未实现异步执行完后采用通知或回调的方法
2、控制多个Future 结果之间的依赖性,如部分完成后进行合并结果等操作
ForkJoin 案例,将大任务分成逻辑相同的小任务,各个任务之间没有依赖。分而治之思想
* 1、ForkJoinPool 执行ForkJoin的线程池,提供多线程异步执行,提供线程Fork和Join操作
* 2、RecursiveAction 不需要返回结果的子任务,RecursiveTask<V> 需要返回结果
* 3、定义业务类继承RecursiveAction或RecursiveTask<V>,在compute()方法中完成任务的Fork和Join操作
- 3.1、RecursiveAction 无返回值案例
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
/**
* @ClassName ForkJoinTest
* @Description ForkJoin 案例,将大任务分成逻辑相同的小任务,各个任务之间没有依赖。分而治之思想
* 1、ForkJoinPool 执行ForkJoin的线程池,提供多线程异步执行,提供线程Fork和Join操作
* 2、RecursiveAction 不需要返回结果的子任务,RecursiveTask<V> 需要返回结果
* 3、定义业务类继承RecursiveAction或RecursiveTask<V>,在compute()方法中完成任务的Fork和Join操作
* 案例:多任务把oldLst数组中的值复制到newLst中。
**/
public class ForkJoinActionTest {
private static int count=10000;
private static List<Integer> oldLst=new ArrayList<>(count);
private static List<Integer> newLst=new ArrayList<>(count);
//初始化就集合值
static {
for(int i=count;i>0;i--){
oldLst.add(i);
//数组需要提前初始化,否则修改不了值
newLst.add(0);
}
}
public static void main(String[] args) {
System.out.println("oldLst:"+oldLst.toString());
System.out.println("newLst:"+newLst.toString());
RecursiveActionTest action=new RecursiveActionTest(0,count,oldLst,newLst);
ForkJoinPool pool=ForkJoinPool.commonPool();
pool.submit(action);
try {
pool.awaitTermination(3000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown();
System.out.println("main is ended");
System.out.println("oldLst:"+oldLst.toString());
System.out.println("newLst:"+newLst.toString());
}
/*
* 不需要返回值的Fork案例;
* 多任务把一个数组中数据复制到另一个数组中*/
private static class RecursiveActionTest extends RecursiveAction{
//子任务阈值大小
private static Integer preSize=100;
private int start;
private int end;
private List<Integer> oldLst;
private List<Integer> newLst;
public RecursiveActionTest(int start,int end,List<Integer> oldLst,List<Integer> newLst){
this.start=start;
this.end=end;
this.oldLst=oldLst;
this.newLst=newLst;
}
@Override
protected void compute() {
if((end-start)<preSize){
System.out.println(Thread.currentThread().getName()+"-start:"+start+",end:"+end);
for(int i=start;i<end;i++){
newLst.set(i, oldLst.get(i));
}
//System.out.println(Thread.currentThread().getName()+"-------执行子任务----"+start+"-this:"+this.hashCode());
}else{
//分成子任务
int middle=(start+end)/2;
RecursiveActionTest action1=new RecursiveActionTest(start,middle,oldLst,newLst);
RecursiveActionTest action2=new RecursiveActionTest(middle,end,oldLst,newLst);
//执行所有子任务
RecursiveActionTest.invokeAll(action1,action2);
//System.out.println(Thread.currentThread().getName()+"========分配任务====="+middle+"-this:"+this.hashCode());
}
}
}
}
- 3.2、RecursiveTask<V> 有返回值案例
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* @ClassName ForkJoinTaskTest
* @Description 采用多任务,对一个数组中的值进行求和运算
**/
public class ForkJoinTaskTest {
private static List<Integer> lst=new ArrayList<>();
static {
//初始化数组中的值
for(int i=0;i<10000;i++){
lst.add(i);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool pool=ForkJoinPool.commonPool();//JDK1.8提供的共用池,默认线程数是CPU核数
ForkJoinTask<Integer> submit = pool.submit(new RecursiveTaskTest(0, lst.size(), lst));
//Fork&Join后获取总结果,堵塞
Integer count = submit.get();
System.out.println("count = " + count);
}
//fork&join 操作,运算数组中所有数的和
private static class RecursiveTaskTest extends RecursiveTask<Integer>{
private int preSize=100;
private int start;
private int end;
private List<Integer> list;
public RecursiveTaskTest(int start,int end,List<Integer> list){
this.start=start;
this.end=end;
this.list=list;
}
@Override
protected Integer compute() {
Integer sum=0;
if(end-start<preSize){
for(int i=start;i<end;i++){
sum=sum+lst.get(i);
}
}else{
int middle=(end+start)/2;
RecursiveTaskTest action1=new RecursiveTaskTest(start,middle,list);
RecursiveTaskTest action2=new RecursiveTaskTest(middle,end,list);
//执行所有任务
RecursiveTaskTest.invokeAll(action1,action2);
//执行Join操作(堵塞)
sum=action1.join()+action2.join();
}
return sum;
}
}
}