Java利用Future获取多线程的运行结果

Java利用Future获取多线程的运行结果

场景:在实际生活中比如有这么一个需求:需要统计A路的长度和B路长度的总和。

示例一:先测量出来A路的长度,再去测量B路的长度,最后把二者相加;(串行)

示例二:叫一个小弟过来帮忙,自己去测量A路的长度,让小弟去测量B路的长度;(并行)

那么以上这两种示例的测量速度哪个快显而易见,肯定是第二种更快。

示例二中就相当于我们开发中的多线程,线程A和线程B可以同步执行各自的任务,而不是任务B需要等待任务A执行完成后才能开始执行。

Future是什么?

在以上例子中,小弟相当于我们开了一个线程B去执行任务,但是如何才能拿到这个线程的返回值呢?这是就引入了Future接口。

Future是一个接口,用于表示异步计算的结果。它提供了一种获取异步计算结果的方法,可以在计算完成之前等待结果的可用性。Future接口有几个常用的方法,包括isDone()用于判断计算是否完成,get()用于获取计算结果(如果计算已完成),cancel()用于取消计算等。

Future的get()方法

Future的get()方法是一个非常常用方法,用于获取该线程的返回值;

示例代码如下:

ExecutorService executor = Executors.newFixedThreadPool(5);
long start = System.currentTimeMillis();
Future<Integer> future1 = executor.submit(() -> {
    Thread.sleep(3000);
    return 20;
});
// 获取第一个线程的结果
Integer first = future1.get();
Future<Integer> future2 = executor.submit(() -> {
    Thread.sleep(5000);
    return 50;
});
// 假设这里还有一段业务代码
Thread.sleep(5000);
// 获取第二个线程的结果
Integer second = future2.get();
long end = System.currentTimeMillis();
System.out.println("两个线程的执行结果为:" + (first + second));
System.out.println("总耗时:" + (end - start) + "ms");

/**
* 结果
* 两个线程的执行结果为:70
* 总耗时:8058ms
*/

需要注意的是线程池中的线程需要实现Callable接口进行返回结果,否则实现Runable接口是没有返回值的

Future中的get()方法有两个实现,源码如下:

/**
 * @throws CancellationException {@inheritDoc}
 */
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

/**
 * @throws CancellationException {@inheritDoc}
 */
public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    return report(s);
}

因为get()方法是会阻塞线程直到线程运行结束的,所以为了避免阻塞时间过长,我们可以调用get(long timeout, TimeUnit unit)这个方法来设置一个超时时间,达到超时时间时会抛出TimeoutException()异常。

这也是Future接口的一个弊端,这里可以对上面的代码稍做优化:

ExecutorService executor = Executors.newFixedThreadPool(5);
long start = System.currentTimeMillis();
Future<Integer> future1 = executor.submit(() -> {
    Thread.sleep(3000);
    return 20;
});
Future<Integer> future2 = executor.submit(() -> {
    Thread.sleep(5000);
    return 50;
});
// 假设这里还有一段业务代码
Thread.sleep(5000);
// 获取第一个线程的结果
Integer first = future1.get();
// 获取第二个线程的结果
Integer second = future2.get();
long end = System.currentTimeMillis();
System.out.println("两个线程的执行结果为:" + (first + second));
System.out.println("总耗时:" + (end - start) + "ms");

/**
* 结果
* 两个线程的执行结果为:70
* 总耗时:5038ms
*/

可以看到时间差距差了3s,所以,非必要时不要去调用Future的get()方法取值,Furue的其他方法也是同理

除了get外,还有以下方法:

  1. cancel 方法:取消任务的执行
  2. isDone方法: 判断线程是否执行完毕
  3. isCanaelled 方法: 判断是否被取消

拓展:如果需要判断该线程是否结束,可以调用Futrue的isDone()方法,会返回一个boolean值

你可能感兴趣的:(Java基础自学笔记,java)