铁锤的IT奇妙探险 Java Back-End Coder

Future解析


本文将会讲解Future类的主要方法和用法。

Future类

运作流程

在图中,右侧是一个线程池,线程池中有一些线程来执行任务。在图的左侧有一个 submit 方法,该方法往线程池中提交了一个 Task,这个 Task 实现了 Callable 接口,调用 submit 方法会立刻返回一个 Future 类型的对象,这个对象目前内容是空的,因为此时计算还没有完成。当计算一旦完成时,线程池便会把这个结果填入到之前返回的Future中去,这时就可以利用 Future 的 get 方法来获取到任务的执行结果了。

方法和用法

Future 接口的代码,一共有 5 个方法

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,TimeoutException
}

get()

get方法在执行时的行为取决于Callable任务的状态,可能会发生以下 5 种情况:

  1. 任务已经执行完毕了,可以立刻返回,获取到任务执行的结果。
  2. 任务还没有结果(未开始或在执行中),去调用get的时候,都会把当前的线程阻塞,直到任务完成再把结果返回。
  3. 任务执行过程中抛出异常,调用get的时候,就会抛出ExecutionException 异常。
  4. 任务被取消了则会抛出 CancellationException。
  5. 调用了这个带延迟参数的get方法之后,如果call方法在规定时间内正常顺利完成了任务,那么get会正常返回;但是如果到达了指定时间依然没有完成任务,get 方法则会抛出 TimeoutException,代表超时了。

isDone()

这个方法如果返回 true 则代表执行完成了,并非一定是成功执行,抛出异常也会返回true;如果返回 false 则代表还没完成。

public class GetException {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(20);
        Future<Integer> future = service.submit(new CallableTask());

        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(i);
                Thread.sleep(500);
            }
            System.out.println(future.isDone());
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class CallableTask implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            throw new IllegalArgumentException("Callable抛出异常");
        }
    }
}

0
1
2
3
4
true
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Callable抛出异常
//对于get而言,抛出的异常仍然时ExecutionException

cancel()

使用cancel方法会有三种情况出现:

  1. 当任务还没有开始执行时,一旦调用 cancel,这个任务就会被正常取消,那么 cancel 方法返回 true。
  2. 如果任务已经完成,或者之前已经被取消过了,那么执行 cancel 方法则代表取消失败,返回 false。
  3. 这个任务正在执行,这个时候执行 cancel 方法是不会直接取消这个任务的,而是会根据我们传入的参数做判断。cancel 方法是必须传入一个参数,该参数叫作  mayInterruptIfRunning,如果值是 true,执行任务的线程就会收到一个中断的信号。如果传入的是 false 则就代表不中断正在运行的任务,也就是说,本次 cancel 不会有任何效果,同时 cancel 方法会返回 false。

isCancelled()

判断是否被取消,它和 cancel 方法配合使用

FutureTask的使用

典型用法是,把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,放到线程池中或另起线程去执行,最后还可以通过 FutureTask 获取任务执行的结果。

/**
 * 描述:     演示 FutureTask 的用法
 */
public class FutureTaskDemo {
	public static void main(String[] args) {
		Task task = new Task();
		FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
		new Thread(integerFutureTask).start();
		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("子线程正在计算");
		int sum = 0;
		for (int i = 0; i < 100; i++) {
			sum += i;
		}
		return sum;
	}
}

Similar Posts

Comments