不知道别人的习惯是如何的,就我个人来说,写程序一般不是有性能问题,很少主动去使用多线程程序。除了主动去优化的场景,直观地认为多线程还是比较麻烦,可能涉及到同步问题,而且一个线程挂了,连累整个进程挂掉。除了可能快点外,多线程没啥大的优点。这样的想法也浪费不少可以优化程序的地方。
Java 中的 Future 用的不算多,知道是 Java 多线程时候保存返回结果的,这两天学习了 Java 并发编程,学到了这个,顺便记录下,印象会深刻点。
一 Future 是什么
我们写多线程程序的时候,任务进程是实现了Runnable
接口,或者直接写个类继承Thread
,但是如果我们需要任务执行完成后,给出个结果,以上两种方式只能通过写共享对象或文件来实现目的,无法直接返回。
还有个缺点,就是Runnable
接口中的run
方法无法抛出异常,我们可能想任务在处理过程中抛出异常,但是Runnable
接口无法抛出异常,就算抛出异常了,我们在哪里接收那,run
接口不是通过主动调用的。
为解决以上两个问题,java 中定义了Callable
接口,接口的具体定义如下:
public interface Callable<V> {
V call() throws Exception;
}
直接了返回值和可以抛出异常,但是这里面就有问题了:
抛出的异常我们如何接收?
返回值我们如何接收,如果每个都有返回值,都需要等待子线程执行完毕,那这个多线程变成同步的了,也没啥意义了。
这就是 Future 需要存在的意义了。Future
是 Java 的接口,类似于容器保存了Callable
的返回结果。我们把子任务放入线程池之后,直接返回,进行其他处理,然后再调用Future
的 get 方法来获取结果,Future
还可以控制子任务的执行。
二 Future 接口方法说明
接口定义如下:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutExceptio
}
get
方法说明:
如果任务执行完毕了,直接返回执行结果。
如果没有执行完成, 则进行阻塞。
如果抛出了异常,则 get 也抛出 ExecutionException 异常。
如果任务被取消了,则抛出 CancellationException 异常。
如果使用 get 带了时间,在规定时间返回结果则正常,如果没有在规定的时间返回结果,则抛出 TimeoutException 异常。
isDone
方法说明:
任务执行完毕返回 true,程序执行失败仍然会返回 true。
未执行完毕返回 false。
cancel
方法说明:
任务未被执行情况下,取消返回 true,被正常取消。
任务被执行,且执行结束情况下,取消失败,返回 false。
如果任务在执行中,如果 cancel 传入 true,则标识中断正在执行的任务 ,传入 false 标识不中断正在执行的任务,等正在执行的任务执行结束,返回 false。
isCancelled
方法说明:
判断任务是否被取消。
三 实践
3.1 一般用法
import java.util.concurrent.*;
public class ExceptionFuture {
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
throw new IllegalArgumentException("异常测试");
}
}
public static void main(String [] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
for (int i = 0; i < 6; i++) {
System.out.println(i);
Thread.sleep(800);
}
System.out.println(future.isDone());
System.out.println("over");
future.get();
}catch(InterruptedException e) {
e.printStackTrace();
}catch(ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
代码执行结果:
说明:
任务中的异常类型会变成 ExecutionException 异常,不过里面带有原来异常信息。
Thread.sleep(800)
是为了让子线程执行完任务,实际使用中我们可以先执行其他任务,执行一段代码后,再去 get,提升整体的 cpu 利用率。
3.2 FutureTask 创建 Future
FutureTask
比较神奇,既可以做为Future
保存执行的结果,又可以作为Task
来传入线程池作为任务执行。
说明:
1.因为 FutureTask 继承的接口,实现了 Runnable ,所以可以执行;又实现了 Future 所以可以保存结果。
实例代码:
import org.activiti.engine.runtime.Execution;
import javax.jws.Oneway;
import java.util.concurrent.*;
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("开始计算任务");
int sum = 0;
for (int i = 0; i <1000; i++) {
sum += i;
}
return sum;
}
}
public class FutureTaskTest {
public static void main(String [] args) throws InterruptedException, ExecutionException {
Task task = new Task();
Callable callable;
FutureTask<Integer> futureTask = new FutureTask<>(task);
ExecutorService executorService = Executors.newFixedThreadPool(4);
// new Thread(futureTask).start();
executorService.submit(futureTask);
System.out.println("sleep and wait");
Thread.sleep(10000);
System.out.println(futureTask.get());
executorService.shutdown();
}
}
执行结果:
四 诗词欣赏
离思五首·其四
[唐代] [元稹]
曾经沧海难为水,除却巫山不是云。
取次花丛懒回顾,半缘修道半缘君。