Future
Future是什么玩意
正如Future的名字,它表示未来可能获得的对象(线程任务submit的返回对象)
Java Future接口是Java多线程编程中的一个接口,它用于表示异步计算的结果。在Java中,通常使用多线程来执行耗时的操作,但是由于多线程中的线程是并发执行的,因此无法保证操作的完成时间。为了解决这个问题,Java提供了Future接口,它可以让我们在等待操作完成时可以继续执行其他的任务,从而提高程序的效率。
Future接口定义了以下方法:
- boolean cancel(boolean mayInterruptIfRunning):尝试取消执行任务,如果任务已经完成或已经被取消,则返回false。
- boolean isCancelled():如果任务已经被取消,则返回true。
- boolean isDone():如果任务已经完成,则返回true。
- V get() throws InterruptedException, ExecutionException:获取任务的计算结果,如果任务还没有完成,则阻塞当前线程,直到任务完成为止。
- V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException:获取任务的计算结果,如果任务还没有完成,则阻塞当前线程,直到任务完成或等待时间超时为止。
在Java中,有多种方式可以创建Future接口的实现类,例如使用ExecutorService.submit(Runnable/Callable)方法提交一个线程任务,返回一个Future对象,通过这个对象可以获取线程任务的执行结果。
总之,Future接口是Java多线程编程中非常重要的一个接口,它可以帮助我们实现异步计算,提高程序的效率。
我们可以用Future来get()任务返回的结果
但是有一个问题, 我线程执行run()方法,而run()方法并没有返回值,那我获取什么?
所以我们引入了Callable
接口
Callable接口
Callable接口可以用于在一个线程中执行某个任务,并返回执行结果。与Runnable接口不同的是,Callable接口可以返回一个值,而Runnable接口不能。
创建一个实现了Callable接口的类,并重写call()方法,该方法返回一个泛型类型的值。
用它替换Runnable接口, 可以运行任务并获得它的返回值(注意是线程池的环境)。
使用Callable接口的案例
public class Main {
public static void main(String[] args) {
// 创建一个单线程的线程池
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Integer> submit = es.submit(new Counter(2, 5));
try {
// 主线程调用get,被阻塞。
System.out.println("计算得到的结果:"+submit.get());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
es.shutdown();
}
}
class Counter implements Callable<Integer> {
private int a;
private int b;
Counter(int a, int b){
this.a = a;
this.b = b;
}
// 实现Callable接口
@Override
public Integer call(){
return a+b;
}
}
输出:
计算得到的结果:7
// 注意,Future对象的get()方法是阻塞的,任务执行完毕并获得结果才会进行下一步操作。
比如上面的案例,会将主线程阻塞。
改进阻塞
如果你希望同时等待多个任务的执行完成并获取结果,可以使用 invokeAll()
方法来一次性提交多个任务,并等待它们的执行完成。这样可以避免在等待每个任务的完成时,阻塞线程池中的线程,从而提高代码的执行效率。
public class Main {
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(5);
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
tasks.add(new Counter(2, 5));
}
List<Future<Integer>> results = es.invokeAll(tasks);
for (Future<Integer> result : results) {
try {
System.out.println("计算得到的结果:" + result.get());
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
System.out.println("主线程继续执行");
es.shutdown();
}
}
class Counter implements Callable<Integer> {
private int a;
private int b;
Counter(int a, int b){
this.a = a;
this.b = b;
}
// 实现Callable接口
@Override
public Integer call(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return a+b;
}
}
上面的例子不会阻塞,可以一次性执行任务。