• 转多线程设计模式


     在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现。

      JDK内置的Future主要使用到了Callable接口和FutureTask类。

      Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。Callable接口的定义如下:

    复制代码
    public interface Callable<V> {
    /**
    * Computes a result, or throws an exception if unable to do so.
    *
    * @return computed result
    * @throws Exception if unable to compute a result
    */
    V call() throws Exception;
    }
    复制代码

      Callable的类型参数是返回值的类型。例如:

    Callable<Integer>表示一个最终返回Integer对象的异步计算。

      Future保存异步计算的结果。实际应用中可以启动一个计算,将Future对象交给某个线程,然后执行其他操作。Future对象的所有者在结果计算好之后就可以获得它。Future接口具有下面的方法:

    复制代码
    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled();
        boolean isDone();
        V get() throws InterruptedException, ExecutionException;
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    }
    复制代码

      第一个get方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个get方法的调用超时,抛出一个TimeoutException异常。如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。如果计算已经完成,那么get方法立即返回。

      如果计算还在进行,isDone方法返回false;如果完成了,则返回true。

      可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果mayInterrupt参数为true,它就被中断。

      FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口。FutureTask有2个构造方法:

    复制代码
    public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
        }
    
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    复制代码

      通常,我们会使用Callable示例构造一个FutureTask对象,并将它提交给线程池进行处理,下面我们将展示这个内置的Future模式的使用。

    复制代码
    public class RealData implements Callable<String> {
        private String param;
        public RealData(String param){
            this.param = param;
        }
        @Override
        public String call() throws Exception {
            StringBuffer sb = new StringBuffer();
            for(int i = 0 ; i< 10 ;i++){
                sb.append(param);
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    
                }
            }
            return sb.toString();
        }
    }
    复制代码

      上述代码实现了Callable接口,它的Call方法会构造我们需要的真实数据并返回,当然这个过程比较缓慢,这里使用Thread.sleep()来模拟它:

    复制代码
    public class FutureMain {
        public static void main(String[] args)
                throws ExecutionException, InterruptedException {
            //构造FutureTask
            FutureTask<String> futureTask = new FutureTask<String>(new RealData("xxx"));
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            //执行FutureTask,发送请求
            //在这里开启线程进行RealData的call()执行
            executorService.submit(futureTask);
    
            System.out.println("请求完毕。。。");
            try {
                //这里可以进行其他额外的操作,这里用sleep代替其他业务的处理
                Thread.sleep(200);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取call()方法的返回值
            //如果此时call()方法没有执行完成,则依然会等待
            System.out.println("真实数据:"+futureTask.get());
        }
    }
    复制代码

      上述代码就是使用Future模式的典型。构造FutureTask时使用Callable接口,告诉FutureTask我们需要的数据应该有返回值。然后将FutureTask提交给线程池,接下来我们不用关心数据是怎么产生的,可以去做其他的业务逻辑处理,然后在需要的时候调用FutureTask.get()得到实际的数据。

      Future模式在日常业务中处理复杂业务时会经常用到,希望大家都能掌握。

    --- 不管多忙,每天给自己预留至少半小时的学习时间,拒绝做代码垃圾的搬运工!
  • 相关阅读:
    Add Two Numbers
    Same Tree
    Single Number
    题目1190:大整数排序
    题目1182:统计单词
    题目1181:遍历链表
    题目1180:对称矩阵
    题目1179:阶乘
    题目1206:字符串连接
    HTML事件
  • 原文地址:https://www.cnblogs.com/dingjin/p/8446213.html
Copyright © 2020-2023  润新知