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调用耗时就不计了。

    走在同样的路上,遇见不一样的风景
  • 相关阅读:
    NOIP2017 时间复杂度 大模拟
    【Python】CV2的一些基本操作
    【Python】类、对象、self
    【Ubuntu18.04】清空回收站
    小飞机可以解决git clone没有返回的问题吗?
    sqlserver2005 远程服务器数据 完全拷贝 到本地数据库
    Microsoft Visual Studio 2005 多线程时 解决不是该线程创建的来访问
    MS SqL2000 数据库置疑状态的解决方法[转]
    vue 函数配置项watch以及函数 $watch 源码分享
    Vue生命周期之beforeCreate vue 生命周期详解
  • 原文地址:https://www.cnblogs.com/flylinran/p/10171449.html
Copyright © 2011-2022 走看看