zoukankan      html  css  js  c++  java
  • FutureTask简单实战

    FutureTask是什么?

    线程池的实现核心之一是FutureTask。在提交任务时,用户实现的Callable实例task会被包装为FutureTask实例ftask;提交后任务异步执行,无需用户关心;当用户需要时,再调用FutureTask#get()获取结果——或异常。

    基本使用

    方法中可能会调用到多个服务/方法,且这些服务/方法之间是互相独立的,不存在先后关系。在高并发场景下,如果执行比较耗时,可以考虑多线程异步的方式调用。

    我们先模拟两个耗时服务

    一个150ms,一个200ms

    public class UserApi {
    
        /** 查询用户基本信息,模拟耗时150ms */
        public String queryUserInfo(long userId) {
            String userInfo = "userInfo: " + userId;
    
            try {
                TimeUnit.MILLISECONDS.sleep(150L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return userInfo;
        }
    
        /** 查询用户地址,模拟耗时200ms */
        public String queryUserAddress(long userId) {
            String userAddress = "userAddress: " + userId;
    
            try {
                TimeUnit.MILLISECONDS.sleep(200L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return userAddress;
        }
    }
    

    不使用FutureTask

    @Test
    public void testNotUseFutureTask() {
        UserApi userApi = new UserApi();
    
        long userId = 12;
        long startTime = System.currentTimeMillis();
    
        // 获取用户基本信息
        String userInfo = userApi.queryUserInfo(userId);
        // 获取用户地址
        String userAddress = userApi.queryUserAddress(userId);
    
        System.err.println("testNotUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
    }
    

    执行几次,结果:

    testNotUseFutureTask 耗时:358
    testNotUseFutureTask 耗时:360
    

    从结果中,可以看到,总耗时是大于queryUserInfoqueryUserAddress之和的。但这两个服务逻辑上并不存在先后关系,理论上最长耗时取决于最慢的那个,即queryUserAddress

    使用FutureTask

    下例使用了FutureTask,来异步调用queryUserInfoqueryUserAddress

    @Test
    public void testUseFutureTask() throws ExecutionException, InterruptedException {
        UserApi userApi = new UserApi();
    
        long userId = 12;
        long startTime = System.currentTimeMillis();
    
        Callable<String> userInfoCallable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                return userApi.queryUserInfo(userId);
            }
        };
        Callable<String> userAddressCallable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                return userApi.queryUserAddress(userId);
            }
        };
        FutureTask<String> userInfoFutureTask = new FutureTask<>(userInfoCallable);
        FutureTask<String> userAddressFutureTask = new FutureTask<>(userAddressCallable);
    
        new Thread(userInfoFutureTask).start();
        new Thread(userAddressFutureTask).start();
    
        String userInfo = userInfoFutureTask.get();
        String userAddress = userAddressFutureTask.get();
        System.err.println("testUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
    }
    
    

    执行几次,结果:

    testUseFutureTask 耗时:239
    testUseFutureTask 耗时:237
    

    很明显,总耗时大大减少了,这就验证了前面所说,总耗时取决于queryUserAddress的耗时。

    实现一个简单的FutureTask

    从前面使用FutureTask的代码中可以看到,一个FutureTask需要包含以下几点:

    1、范型
    2、构造函数,传入Callable
    3、实现Runnable
    4、有返回值
    
    • MyFutureTask代码如下:
    public class MyFutureTask<T> implements Runnable {
    
        private Callable<T> callable;
        private T           result;
        private String      state;
    
        public MyFutureTask(Callable<T> callable) {
            this.callable = callable;
        }
    
        @Override
        public void run() {
            state = "NEW";
            try {
                result = callable.call();
            } catch (Exception e) {
                e.printStackTrace();
            }
            state = "DONE";
            synchronized (this) {
                this.notify();
            }
        }
    
        /** 获取调用结果 */
        public T get() throws InterruptedException {
            if ("DOEN".equals(state)) {
                return result;
            }
            synchronized (this) {
                this.wait();
            }
            return result;
        }
    }
    
    • 使用:
    @Test
    public void testMyUseFutureTask() throws InterruptedException {
        UserApi userApi = new UserApi();
    
        long userId = 12;
        long startTime = System.currentTimeMillis();
    
        Callable<String> userInfoCallable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                return userApi.queryUserInfo(userId);
            }
        };
        Callable<String> userAddressCallable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                return userApi.queryUserAddress(userId);
            }
        };
        
        // 不同点
        MyFutureTask<String> userInfoFutureTask = new MyFutureTask<>(userInfoCallable);
        MyFutureTask<String> userAddressFutureTask = new MyFutureTask<>(userAddressCallable);
    
        new Thread(userInfoFutureTask).start();
        new Thread(userAddressFutureTask).start();
    
        String userInfo = userInfoFutureTask.get();
        String userAddress = userAddressFutureTask.get();
        System.err.println("testMyUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
    }
    
    • 输出结果:
    testMyUseFutureTask 耗时:208
    testMyUseFutureTask 耗时:211
    

    从结果中看到,预期与使用FutureTask的一致。至于使用我们自定义的MyFutureTask执行耗时为何会比FutureTask长,我猜测是我们自己写的未做更多的检查和判断。我们自己写的只是用来学习FutureTask

    总结

    不使用异步的方式时,queryUserAddressqueryUserInfo执行之后才会执行,两者相加的时间算入总调用耗时。如果使用了异步线程调用,由于queryUserAddress耗时长,这样在queryUserAddress执行结束前,queryUserInfo就执行结束了,这样queryUserInfo调用耗时就不计了。

    走在同样的路上,遇见不一样的风景
  • 相关阅读:
    微软企业库4.1学习笔记(二十六)Unity依赖注入模块3
    微软企业库4.1学习笔记(三十七)日志模块 在应用中使用日志模块
    微软企业库5.0学习笔记(三十五)数据访问模块 DataSet以及数据库事务
    微软企业库4.1学习笔记(四十一)依赖注入模块Unity 简介
    项目统一开发管理解决方案思路[项目组成员同时做很多项目的解决思路探讨]
    在moss2007WEB应用服务器上发布独立web程序时遇到的问题的解决思路
    工作流表单自定义的误区
    文档库创建的子文件夹的URL显示为 http://[机器名]/.... 导致无法正常访问的问题解决办法
    申请加入 .NET企业应用开发 博客团队请回复
    儿子照片
  • 原文地址:https://www.cnblogs.com/flylinran/p/10171449.html
Copyright © 2011-2022 走看看