你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

【架构师面试-JUC并发编程-11】-Future和FutureTask

2021-11-6 2:10:45

1:Future类

FutureTask叫未来任务,可以将一个复杂的任务剔除出去交给另外一个线程来完成

 

图示为main方法开始,依次调用多个方法。但是第3个方法复杂,为避免其是瓶颈影响整个程序效率,所以将其剔除出去交给FutureTask去完成,这样避免阻塞主线程。

2:Callable和Future的关系

可以用Future.get来获取Callable接口返回的执行结果,还可以通过Future.isDone()来判断任务是否已经执行完,以及取消这个任务,限时获取任务的结果等。

在call()未执行完毕前,调用get()的线程(假设此时是主线程)会被阻塞,直到call()方法返回结果后,此时future.get()才会得到该结果,然后主线程才会切换到runnable状态。

所以Future是一个存储器,它存储了call()这个任务的结果,而这个任务的执行时间是无法确定的,因为这完全取决于call()方法的执行情况。

3:Future主要方法

1:get()

get方法的行为取决于Callable任务的状态,只有以下5种情况:

1)任务正常完成:get方法会立刻返回结果

2)任务尚未完成(任务还没有开始或进行中):get将阻塞并直到任务完成。

3)任务执行过程中抛出Exception:get方法会抛出ExecutionException,这里抛出异常,是call()执行时产生的那个异常,

看到这个异常类型是java.util.concurrent.ExecutionException.不论call()执行时抛出的异常类型是什么,最后get方法会抛出的异常类型都是ExceutionException

4)任务被取消:get方法会抛出CancellationException

5)任务超时:get方法有一个重写方法,是传入一个延迟时间的,如果时间到了还没有获得结果,get方法会抛出TimeoutException

2:get(long timeout,TimeUnit unit)

如果call()在规定时间内完成任务,那么就会正常获取到返回值,而如果在指定时间内没有计算出结果,则会抛出TimeoutException

超时不获取,任务需要取消

3:cancel()

如果这个任务还没有开始执行,任务会被正常取消,未来也不会被执行,返回true

如果任务已经完成或已经取消,则cancel()方法会执行失败,方法返回false

如果这个任务已经开始,这个取消方法将不会直接取消该任务,而是会根据参数mayInterruptIfRunningg来做判断。如果是true,就会发出中断信号给这个任务。

4:isDone()

判断线程是否执行完,执行完并不代表执行成功。

5:isCancelled()

判断是否被取消

4:用线程池的submit方法返回Future对象

首先要给线程池提交任务,提交时线程池会立刻返回一个空的Future容器。当线程的任务一旦执行完,也就是当我们可以获取结果时,线程池会把该结果填入到之前给我们的那个Future中去(而不是创建一个新的Future),我们此时可以从该Future中获得任务执行的结果。

 

future/OneFuture.java

package future;
 
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
/**
 * 描述:     演示一个Future的使用方法
 */
public class OneFuture {
 
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        Future<Integer> future = service.submit(new CallableTask());
        try {
            //等待3秒后打印
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }
 
    static class CallableTask implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            return new Random().nextInt();
        }
    }
 
}

5:用FutureTask来创建Future

用FutureTask来获取Future和任务的结果。

FutureTask实现Runnable和Future接口

 把Callable实例当作参数,生成FutureTask的对象,然后把这个对象当作一个Runnable对象,用线程池去执行这个Runnable对象,最后通过FutureTask获取刚才执行的结果。

future/ FutureTaskDemo.java

package future;
 
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
 
/**
 * 描述:     演示FutureTask的用法
 */
public class FutureTaskDemo {
 
    public static void main(String[] args) {
        Task task = new Task();
        //FutureTask继承Future和Runnalbe接口
        FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
//        new Thread(integerFutureTask).start();
        ExecutorService service = Executors.newCachedThreadPool();
        service.submit(integerFutureTask);
 
        try {
            System.out.println("task运行结果:"+integerFutureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
 
class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程正在计算");
        Thread.sleep(3000);
        //模拟子线程处理业务逻辑
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}