【并发编程】Java5 - Future,基本使用

1. 简介

  在使用多线程开发中,不论是继承Thread类还是实现Runnable接口方式,都无法非常方便的获取异步任务执行的结果。在JDK1.5提供了和Runnable类似但多了返回值的Callable接口,通过Future接口实现类和Callable接口方式,可以非常灵活的进行多线程操作,例如:获取结果、指定超时时间获取结果、取消任务、判断是否取消、判断是否完成等。

2. Future接口API

方法及参数 描述
boolean cancel(boolean mayInterruptIfRunning) 尝试取消此任务。如果任务正在执行,mayInterruptIfRunning为false则不会取消任务而是继续执行只到完成,为true则会取消任务;如果任务未开始执行,不论mayInterruptIfRunning为false还是true,均会取消任务
boolean isCancelled() 如果此任务在正常完成之前被取消,则返回true
boolean isDone() 如果此任务完成,则返回true 。正常终止、异常或取消此方法都将返回true
V get() 获取结果
V get(long timeout, TimeUnit unit) 获取结果,超时自动退出

3. FutureTask简介

  FutureTask为Future接口的实现类,提供了Future的所有功能,并且可以将Callable和Runnable包装成FutureTask并交给Executor执行。

  通过FutureTask类图可以看出,RunnableFuture类实现了RunnableFuture接口,RunnableFuture接口又继承自Future接口和Runnable接口。

/**
 * Creates a {@code FutureTask} that will, upon running, execute the
 * given {@code Callable}.
 *
 * @param  callable the callable task
 * @throws NullPointerException if the callable is null
 */
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

/**
 * Creates a {@code FutureTask} that will, upon running, execute the
 * given {@code Runnable}, and arrange that {@code get} will return the
 * given result on successful completion.
 *
 * @param runnable the runnable task
 * @param result the result to return on successful completion. If
 * you don't need a particular result, consider using
 * constructions of the form:
 * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
 * @throws NullPointerException if the runnable is null
 */
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

  FutureTask类提供了两个构造函数。当传入Callable接口的实现类时,可以通过Callable的run方法获取返回值;当存入Runnable接口时,还可以通过result参数自行指定返回值,当然如果并不需要返回值则result参数传null即可。

4. FutureTask应用

  • 前期准备:分别实现Callable和Runable接口创建线程
/**
 * 创建线程
 */
class MyRunnable implements Runnable {

    @Override
    public void run() {
        log.info("正在执行任务...");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 创建线程
 */
class MyCallable implements Callable<String> {

    @Override
    public String call() {
        log.info("正在执行任务...");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "result";
    }
}
  • Callable + FutureTask
/**
 * Callable + FutureTask
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCallable() throws ExecutionException, InterruptedException {
    MyCallable myCallable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(myCallable);
    new Thread(futureTask).start();
    String result = futureTask.get();
    log.info("返回结果:" + result);

    // 使用Executors将Runnable转换为Callable
    // Executors.callable(Runnable task, T result) 可以自定义返回值
    // Executors.callable(Runnable task) 返回值为null
    MyRunnable myRunnable = new MyRunnable();
    Callable myCallable2 = Executors.callable(myRunnable, "custom result2");
    FutureTask<String> futureTask2 = new FutureTask<>(myCallable2);
    new Thread(futureTask2).start();
    String result2 = futureTask2.get();
    log.info("返回结果2:" + result2);
}

  控制台打印:

22:12:43.630 [Thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:12:44.637 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result
22:12:44.641 [Thread-2] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:12:45.641 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2
  • Runnable + FutureTask
/**
 * Runnable + FutureTask
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testRunnable() throws ExecutionException, InterruptedException {
    MyRunnable myRunnable = new MyRunnable();
    FutureTask<String> futureTask = new FutureTask<>(myRunnable, "custom result");
    new Thread(futureTask).start();
    String result = futureTask.get();
    log.info("返回结果:" + result);
}

  控制台打印:

22:14:04.289 [Thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:14:05.296 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:custom result
  • Future + ExecutorService
/**
 * Future + ExecutorService
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testFuture() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newSingleThreadExecutor();

    MyCallable myCallable = new MyCallable();
    Future<String> future = executorService.submit(myCallable);
    String result = future.get();
    log.info("返回结果:" + result);

    MyRunnable myRunnable = new MyRunnable();
    Future<String> future2 = executorService.submit(myRunnable, "custom result2");
    String result2 = future2.get();
    log.info("返回结果2:" + result2);

    executorService.shutdown();
}

  控制台打印:

22:14:42.179 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:14:43.185 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result
22:14:43.187 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:14:44.187 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2
  • FutureTask + ExecutorService
/**
 * FutureTask + ExecutorService
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testFutureTask() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newSingleThreadExecutor();

    MyCallable myCallable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(myCallable);
    executorService.submit(futureTask);
    String result = futureTask.get();
    log.info("返回结果:" + result);

    MyRunnable myRunnable = new MyRunnable();
    FutureTask<String> futureTask2 = new FutureTask<>(myRunnable, "custom result2");
    executorService.submit(futureTask2);
    String result2 = futureTask2.get();
    log.info("返回结果2:" + result2);

    executorService.shutdown();
}

  控制台打印:

22:15:30.719 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:15:31.725 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result
22:15:31.727 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务...
22:15:32.728 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2

5. FutureTask任务取消

  背景:创建两个任务,将两个任务依次添加到单一线程池中,此时任务1正在执行,任务2正在等待。

  • 前期准备:实现Callable接口创建线程
/**
 * 创建线程
 */
@RequiredArgsConstructor
class MyCallable implements Callable<String> {

    private final String taskName;

    @Override
    public String call() throws Exception {
        log.info(taskName + "开始执行");
        Thread.sleep(1000);
        log.info(taskName + "结束执行");
        return null;
    }
}
  • 取消任务1,mayInterruptIfRunning设置为true
/**
 * 任务1开始执行,任务2未执行。<br/>
 * 取消任务1,mayInterruptIfRunning设置为true
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCancalTask1ForTrue() throws ExecutionException, InterruptedException {
    FutureTask futureTask1 = new FutureTask(new MyCallable("任务1"));
    FutureTask futureTask2 = new FutureTask(new MyCallable("任务2"));

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(futureTask1);
    executorService.submit(futureTask2);

    // 保证任务1开始执行
    Thread.sleep(500);

    futureTask1.cancel(true);
    futureTask2.get();

    executorService.shutdown();
}

  控制台打印:

23:06:01.946 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行
23:06:02.446 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2开始执行
23:06:03.446 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2结束执行

  通过日志可以发现,任务1被取消成功。

  • 取消任务1,mayInterruptIfRunning设置为false
/**
 * 任务1开始执行,任务2未执行。<br/>
 * 取消任务1,mayInterruptIfRunning设置为false
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCancalTask1ForFalse() throws ExecutionException, InterruptedException {
    FutureTask futureTask1 = new FutureTask(new MyCallable("任务1"));
    FutureTask futureTask2 = new FutureTask(new MyCallable("任务2"));

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(futureTask1);
    executorService.submit(futureTask2);

    // 保证任务1开始执行
    Thread.sleep(500);

    futureTask1.cancel(false);
    futureTask2.get();

    executorService.shutdown();
}

  控制台打印:

23:05:59.926 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行
23:06:00.932 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行
23:06:00.932 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2开始执行
23:06:01.933 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2结束执行

  通过日志可以发现,任务1被取消失败。

  • 取消任务2,mayInterruptIfRunning设置为true
/**
 * 任务1开始执行,任务2未执行。<br/>
 * 取消任务2,mayInterruptIfRunning设置为true
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCancalTask2ForTrue() throws ExecutionException, InterruptedException {
    FutureTask futureTask1 = new FutureTask(new MyCallable("任务1"));
    FutureTask futureTask2 = new FutureTask(new MyCallable("任务2"));

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(futureTask1);
    executorService.submit(futureTask2);

    // 保证任务1开始执行
    Thread.sleep(500);

    futureTask1.get();
    futureTask2.cancel(true);

    executorService.shutdown();
}

  控制台打印:

23:10:09.233 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行
23:10:10.238 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行

  通过日志可以发现,任务2被取消成功。

  • 取消任务2,mayInterruptIfRunning设置为false
/**
 * 任务1开始执行,任务2未执行。<br/>
 * 取消任务2,mayInterruptIfRunning设置为false
 *
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCancalTask2ForFalse() throws ExecutionException, InterruptedException {
    FutureTask futureTask1 = new FutureTask(new MyCallable("任务1"));
    FutureTask futureTask2 = new FutureTask(new MyCallable("任务2"));

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(futureTask1);
    executorService.submit(futureTask2);

    // 保证任务1开始执行
    Thread.sleep(500);

    futureTask1.get();
    futureTask2.cancel(false);

    executorService.shutdown();
}

  控制台打印:

23:11:03.170 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行
23:11:04.177 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行

  通过日志可以发现,任务2被取消成功。

  结论:

mayInterruptIfRunning 正在执行的任务 等待中的任务
true 成功 成功
false 失败 成功

6. FutureTask优缺点

  前期准备:实现Callable接口创建线程,并指定每个任务的执行时间。

/**
 * 创建线程
 */
@RequiredArgsConstructor
class MyCallable implements Callable<Long> {

    /**
     * 任务执行时间
     */
    private final long execTime;

    @Override
    public Long call() throws Exception {
        Thread.sleep(execTime);
        return execTime;
    }
}
6.1 优点
  • 并行执行多个任务
/**
 * 优点:并行执行多个任务
 */
@Test
public void testConcurrent() {
    ExecutorService executorService = Executors.newFixedThreadPool(3);

    List<Future<Long>> futureList = new ArrayList<>(3);

    // 记录开始时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    // 创建3个任务
    futureList.add(executorService.submit(new MyCallable(3000)));
    futureList.add(executorService.submit(new MyCallable(3000)));
    futureList.add(executorService.submit(new MyCallable(3000)));

    // 阻塞获取结果
    futureList.forEach(f -> {
        try {
            f.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    });

    stopWatch.stop();
    log.info("总耗时:" + stopWatch.getTotalTimeMillis() + " ms");
}

  控制台打印:

23:27:49.398 [main] INFO com.c3stones.test.FutureTest - 总耗时:3006 ms

  可以看出,3个任务并行执行,只需要耗时大约一个任务的时间。

6.2 缺点
  • 使用get()方法获取结果会一直阻塞
/**
 * 缺点:获取结果会一直阻塞
 */
@Test
public void testGet() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(1);

    FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000));
    executorService.submit(futureTask);

    Long result = futureTask.get();

    log.info("返回结果:" + result);
}

  等待60秒,控制台打印:

00:03:48.954 [main] INFO com.c3stones.test.FutureTest - 返回结果:60000

  查看get()方法源码:

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

  继续查看awaitDone方法源码:

/**
 * Awaits completion or aborts on interrupt or timeout.
 *
 * @param timed true if use timed waits
 * @param nanos time to wait, if timed
 * @return state upon completion
 */
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        else if (q == null)
            q = new WaitNode();
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this);
    }
}

  通过DEBUG跟踪代码可以发现,最终会调用LockSupport.park(Object blocker)方法。用LockSupport.park()和Thread.sleep()类似,都会阻塞当前线程,且都不会释放当前线程占有的锁资源,区别在于LockSupport.park()方法会被其他线程调用LockSupport.unpark()方法唤醒,或调用Thread.interrupt()方法唤醒。
  当我们在代码中使用get()方法等待结果时,并不能处理其他的任务,相当于同步执行。

  • 使用get(long timeout, TimeUnit unit)方法超时会抛出TimeoutException异常
/**
 * 缺点:超时抛出TimeoutException异常
 */
@Test
public void testGetTimeout() throws ExecutionException, InterruptedException, TimeoutException {
    ExecutorService executorService = Executors.newFixedThreadPool(1);

    FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000));
    executorService.submit(futureTask);

    Long result = futureTask.get(1000, TimeUnit.MILLISECONDS);


    log.info("返回结果:" + result);
}

  等待1秒,控制台打印:

java.util.concurrent.TimeoutException
	at java.util.concurrent.FutureTask.get(FutureTask.java:205)
	at com.c3stones.test.FutureTest.testGetTimeout(FutureTest.java:96)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
  • 轮询获取结果造成CPU空转,浪费资源
/**
 * 缺点:轮询获取结果造成CPU空转,浪费资源
 */
@Test
public void testRoundGet() throws ExecutionException, InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(1);

    FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000));
    executorService.submit(futureTask);

    // 可以做其他事
    // ...

    // 最后轮询判断任务是否结束,结束后获取结果
    Long result;
    while (true) {
        if (futureTask.isDone()) {
            result = futureTask.get();
            break;
        }
    }

    log.info("返回结果:" + result);
}

  等待60秒,控制台打印:

00:11:19.338 [main] INFO com.c3stones.test.FutureTest - 返回结果:60000

  综上所述问题,Guava工具包提供了ListenableFuture,随后JDK1.8也推出CompletableFuture。
  详情请浏览:
  【并发编程】Java5 - CompletionService,将异步执行与获取结果分离
  【并发编程】Guava - ListenableFuture,避免Future获取阻塞问题,增加回调
  【并发编程】Java8 - CompletableFuture,增强版Future

7. 项目地址

  thread-demo

posted @ 2023-03-28 16:54  C3Stones  阅读(80)  评论(0编辑  收藏  举报