Future
我们在Callable接口的学习中,简单谈过Futrue,接下来看看到底是什么东西
一、Futrue介绍
Future
在Java中是一个接口:实现Future
表示具有异步地对线程进行计算的能力
API设计如下:
boolean cancel(boolean mayInterruptIfRunning) //试图取消此任务的执行。
V get() // 等待线程,直到任务完成,返回结果
V get(long timeout, TimeUnit unit) //指定等待时间,当中任务完成,返回结果,否则抛出异常
boolean isCancelled() // 判断任务是否被取消了,如果调用了cance()则返回true
boolean isDone() //如果任务完成,则返回ture。 任务完成包含正常终止、异常、取消任务。在这些情况下都返回true
1.1、FutrueTask
那到底是如何计算的呢?有什么作用吗?来看看这个实现类FutureTask
一、状态码介绍
// 取自FutureTask源码
private volatile int state;
private static final int NEW = 0; // 表示是个新的任务或者还没被执行完的任务。这是初始状态。
private static final int COMPLETING = 1; //
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
以下详解取自网上(知道的告诉我,给他个链接)
- NEW :表示是个新的任务或者还没被执行完的任务。这是初始状态。
- COMPLETING :任务已经执行完成或者执行任务的时候发生异常,但是任务执行结果或者异常原因还没有保存到outcome字段
- outcome字段用来保存任务执行结果
- 如果任务发生异常,则用来保存异常原因的时候,状态会从NEW变更到COMPLETING。但是这个状态会时间会比较短,属于中间状态。
- outcome字段用来保存任务执行结果
- NORMAL :任务已经执行完成并且任务执行结果已经保存到outcome字段,状态会从COMPLETING转换到NORMAL。这是一个最终态。
- EXCEPTIONAL :任务执行发生异常并且异常原因已经保存到outcome字段中后,状态会从COMPLETING转换到EXCEPTIONAL。这是一个最终态。
- CANCELLED :任务还没开始执行或者已经开始执行但是还没有执行完成的时候,用户调用了cancel(false)方法取消任务,且不中断任务执行线程,这个时候状态会从NEW转化为CANCELLED状态。这是一个最终态。
- INTERRUPTING :任务还没开始执行或者已经执行但是还没有执行完成的时候,用户调用了cancel(true)方法取消任务并且要中断任务执行线程但是还没有中断任务执行线程之前,状态会从NEW转化为INTERRUPTING。这是一个中间状态。
- INTERRUPTED :调用interrupt()中断任务执行线程之后状态会从INTERRUPTING转换到INTERRUPTED。这是一个最终态。
FutrueTask保存了任务的各个状态,再由于实现了Future
接口,那么就可调用Future
相关方法根据任务进行异步处理
记住一点:当任务抛出异常并不会影响线程中断,这个异常是从任务抛出去的,而线程是由任务调取的
二、简单使用一把
class MyThread implements Callable<String> {
@Override
public String call() {
System.out.println("call");
return "Hello Callable";
}
}
Thread
线程类的有参构造器只能接收Runnable
接口的实现类
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask<String> task = new FutureTask<String>(myThread);
new Thread(task).start();
String s = task.get();// 获取Callable返回的结果
System.out.println(s);
}
输出:
三、测试状态改变
当调用同个任务执行时
package com.migu;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Cache cache = new Cache();
FutureTask<Integer> task = new FutureTask<>(cache);
FutureTask<Integer> task2 = new FutureTask<>(cache);
new Thread(task).start();
new Thread(task).start(); // 执行相同的任务
new Thread(task2).start();
TimeUnit.SECONDS.sleep(1);
System.out.println("task: " + task.get()); // out: 1
System.out.println("task2: " + task2.get()); // out: 2
}
}
class Cache implements Callable<Integer> {
public int i;
@Override
public Integer call() {
++i;
return i;
}
}
对以上输出进行解析:为什么一共执行三次任务,i
的值却为2
-
上面提到过:任务会保存着当前所持有线程的状态
state
。,然后来看看这段代码public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; .... }
当线程第二次调用相同任务运行时,发现当前任务的
state
,已不为NEW
状态,那么该线程会直接结束,任务也不再执行 -
当获取
FutureTask
的返回值时,是存在阻塞的- 因为他要一直等待call方法结束才能得到返回值
- 最好的办法就是:尽量放在最后一行,和使用异步计算解决
下面将介绍代替Thread
类执行任务的技术
1.2、CompletableFuture
需了解函数式接口和线程池使用
一、CompletableFuture介绍
CompletableFuture可以通过与线程池搭配执行任务
可以在原有任务上执行其他任务,且可以并行执行,那么我们就可以把原先其他无关的任务放在当中
二、使用
新学习的玩意:
- supplyAsync方法:可以返回结果,参数是一个函数式接口
- 重载方法,可直接传入一个线程池,达到运行多个任务的能力
- whenCompleteAsync方法:可以将该方法内实现交给其他线程执行
- 还有一个whenComplete是让当前线程执行的,那这样的话就会阻塞了,就没有我们想要的并行效果
- 更改API可查看具体文档
package com.migu.complate;
import java.util.concurrent.*;
public class Test3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
for (int i = 0; i < 3; i++) {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 1024;
}, pool).whenCompleteAsync((result, e) -> {
// 这边做其他任务----------
for (int i = 0; i < 3; i++) {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 我在干其他事");
}
//------------------
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 1028;
}, pool).whenCompleteAsync((result, e) -> {
// 这边做其他任务----------
for (int i = 0; i < 3; i++) {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 我在干其他事");
}
//------------------
});
pool.shutdown();
System.out.println(future.get()); // get方法依然会阻塞,但我们可以把其他实现放进任务中
System.out.println(future2.get());
}
}
输出:
1.3、FutrueTask结合线程池使用
这边再提一下吧
- 可同CompletableFutrue一样,异步的对其执行任务
- 但是不能在执行同任务无关的方法了(我现在只能这样解释)
package com.migu.complate;
import java.util.concurrent.*;
public class Test4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
FutureTask task = new FutureTask<Integer>(()->{
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 10;
});
FutureTask task2 = new FutureTask<Integer>(()->{
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 11;
});
pool.submit(task);
pool.submit(task2);
pool.shutdown();
System.out.println("第一条任务执行完毕: " + task.get());
System.out.println("第二条任务执行完毕: " + task2.get());
}
}
out:
1.4、ExecutorCompletionService
一、ExecutorCompletionService介绍
该类可以实现按照任务完成的先后顺序获取任务结果
二、使用
package com.migu.complate;
import java.util.concurrent.*;
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
ExecutorCompletionService<Integer> service = new ExecutorCompletionService<Integer>(pool);
service.submit(()->{
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 1024;
});
service.submit(()->{
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + ": " + i);
}
return 250;
});
pool.shutdown();
System.out.println("-------who am i-----------");
// 依然阻塞,但是谁先结束获取谁的结果
System.out.println(service.take().get());
System.out.println(service.take().get());
System.out.println("-------who am i-----------");
}
}
输出:由于任务1,休眠实现比任务二短,则先执行