异步(一)Future

在开发应用系统过程中,通过异步并发并不能使执行速度变快,更多的时候是为了通过并发充分利用CPU以及提升系统的吞吐量

一、Future接口

Future接口在java.util.concurrent包下,它用来处理异步任务的执行结果,在异步任务执行完成之前会阻塞该方法。

方法摘要:

get: 用户获取任务执行结果

cancel: 取消执行的任务

isDone: 判断是否完成

isCancelled: 判断是否主动取消

二、异步实现

下面使用线程池配合Future实现该示例,但是请注意:阻塞请求主线程,高并发时候仍然会造成线程数过多,CPU上下文切换。通过Future可以并发出N个请求,然后等待最慢的一个返回,总响应时间为最慢的一个请求返回的结果用时。

代码示例:

import java.util.concurrent.*;

/**
 * @author lay
 * @date 2018/5/28.
 * @time 00:43
 */
public class FutureTest {

    public static void main(String[] args){
        // 创建固定线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        long start = System.currentTimeMillis();
        // 提交执行两个线程
        Future<String> future = executorService.submit(new CallableTask("task one", 1000));
        Future<String> future2 = executorService.submit(new CallableTask("task two", 2000));
        try {
            System.out.println(future.get());
            System.out.println(future2.get());
        } catch (Exception e) {
            if (future != null) {
                // 取消执行
                future.cancel(true);    
            }
            if (future2 != null) {
                // 取消执行
                future2.cancel(true);    
            }
            // 抛出回滚
            throw new RuntimeException(e);
        }
        
        System.out.println("总共消耗时间:" + (System.currentTimeMillis() - start));
        // 关闭线程池
        executorService.shutdown();
    }
}

class CallableTask implements Callable<String> {

    private String taskName;

    private long exeTime;

    public CallableTask(String taskName, long exeTime) {
        this.taskName = taskName;
        this.exeTime = exeTime;
    }

    @Override
    public String call() throws Exception {
        System.out.println(taskName + " is executing");
        Thread.sleep(exeTime);
        return taskName + " is end";
    }
}

以上代码输出结果:

task one is executing
task two is executing
task one is end
task two is end
总共消耗时间:2011

1)我们通过Executors创建了一个可以容纳两个线程的线程池;

2)提交两个实现了Callable接口的异步类实例到线程池,并执行;

3)通过Future来处理两个线程的执行结果,并打印;

4)如果发生异常则取消执行,并抛出运行时异常;

5)关闭线程池

注意:我们看到总共消耗时间约等于2秒,也就是等同于两个线程耗时最长的那个线程。通过输出结果我们可以了解到,两个线程被执行的时候,主线程阻塞等待线程结果。

参考:《亿级流量网站架构核心技术》—— 张开涛

猜你喜欢

转载自www.cnblogs.com/lay2017/p/9098297.html