• 线程技术 ☞ Future模式


     线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式。

      Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个程序执行流里某个操作特别耗时,我们可以另起一个线程专门执行这个繁琐耗时的任务,主线程则可以做其他的事情,下面是我自己找到的一个实现原生Future模式的代码,它主要参入者如下:

      TestMain.java:测试程序入口,主要是调用Client类,向Client发送请求;

      Client.java:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData;

      Data.java:返回数据接口;

      FutureData.java:Future数据,构造快,但是是一个虚拟的数据,需要装配RealData;

      RealData.java:真实数据,其构造是比较慢的。

      详细代码如下:

    Data接口:

    package cn.com.xSharp.futurePattern.simple;
     
    /**
     * 数据接口
     */
    public interface Data {
        public String getData();
    }

    RealData代码:

    package cn.com.xSharp.futurePattern.simple;
     
    /**
     * RealData是最终使用的数据,它构造很慢,因此用sleep来模拟
     */
    public class RealData implements Data {
         
        protected final String result;
         
        public RealData(String param) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0;i < 10;i++){
                sb.append(param);
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            result = sb.toString();
        }
     
        @Override
        public String getData() {
            return result;
        }
    }

    FutureData代码:

    package cn.com.xSharp.futurePattern.simple;
     
    public class FutureData implements Data {
         
        protected RealData realData = null;// FutureData对RealData的包装
        protected boolean isReady = false;
         
        public synchronized void setRealData(RealData realData){
            if (isReady){
                return;
            }
            this.realData = realData;
            isReady = true;
            notifyAll();
        }
     
        @Override
        public synchronized String getData() {
            while (!isReady){
                try {
                    wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return realData.result;
        }
     
    }

    Client代码:

    package cn.com.xSharp.futurePattern.simple;
     
    public class Client {
     
        public Data request(final String qryStr){
            final FutureData futureData = new FutureData();
            new Thread(){
                public void run(){
                    RealData realData = new RealData(qryStr);
                    futureData.setRealData(realData);
                }
            }.start();
            return futureData;
        }
    }

    TestMain代码:

    package cn.com.xSharp.futurePattern.simple;
     
    public class TestMain {
     
        public static void main(String[] args) {
            Client client = new Client();
            Data data = client.request("xtq");
            System.out.println("请求完毕!");
             
            try {
                for (int i = 0;i < 12;i++){
                    Thread.sleep(100);
                    System.out.println("可以做做其他的事情哦....");
                }
                 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             
            System.out.println("数据==:" + data.getData());
        }
     
    }

    执行结果:

     1 请求完毕!
     2 可以做做其他的事情哦....
     3 可以做做其他的事情哦....
     4 可以做做其他的事情哦....
     5 可以做做其他的事情哦....
     6 可以做做其他的事情哦....
     7 可以做做其他的事情哦....
     8 可以做做其他的事情哦....
     9 可以做做其他的事情哦....
    10 可以做做其他的事情哦....
    11 可以做做其他的事情哦....
    12 可以做做其他的事情哦....
    13 可以做做其他的事情哦....
    14 数据==:xtqxtqxtqxtqxtqxtqxtqxtqxtqxtq

     JDK里在1.5之后提供了专门Future模式的实现,这里我使用FutureTask来实现Future模式。

      FutureTask在JDK文档里的解释:

      可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。 可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。 除了作为一个独立的类外,此类还提供了 protected 功能,这在创建自定义任务类时可能很有用。

      下面是它的两个构造函数:

    FutureTask(Callable<V> callable)
              创建一个 FutureTask,一旦运行就执行给定的 Callable。
    FutureTask(Runnable runnable, V result)
              创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。

      这里我首先使用第二个构造函数Runnable实现Future模式,代码如下:

    package cn.com.futuretest;
     
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.FutureTask;
     
    public class FutureRunnable implements Runnable{
        private Result result;  // 操作的数据,模拟一个计算需要很长时间的数据
     
        /* 初始化数据 */
        public FutureRunnable(Result result) {
            this.result = result;
        }
     
        @Override
        public void run() {
            try {
                for (int i = 0;i < 10;i++){
                    Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
                    result.setData(result.getData() + ":" + "futureRunnable" + i);
                }                      
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
         
        public static void main(String[] args) {
            Result r = new Result("xSharp");// 构造测试数据
            FutureRunnable futureCallable = new FutureRunnable(r);// 初始化runnable
            FutureTask<Result> task = new FutureTask<Result>(futureCallable, r);
            // 构造固定大小为一个线程的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            // 执行线程
            executorService.execute(task);
            System.out.println("执行完毕!");
             
            try {
                for (int i = 0;i < 15;i++){
                    Thread.sleep(100);
                    System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             
            try {
                System.out.println("打印结果是:" + task.get().getData());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }finally{
                System.exit(0);
            }
        }
    }

     执行结果: 

    执行完毕!
    数据还在计算中等待中,你可以做别的事情0
    数据还在计算中等待中,你可以做别的事情1
    数据还在计算中等待中,你可以做别的事情2
    数据还在计算中等待中,你可以做别的事情3
    数据还在计算中等待中,你可以做别的事情4
    数据还在计算中等待中,你可以做别的事情5
    数据还在计算中等待中,你可以做别的事情6
    数据还在计算中等待中,你可以做别的事情7
    数据还在计算中等待中,你可以做别的事情8
    数据还在计算中等待中,你可以做别的事情9
    数据还在计算中等待中,你可以做别的事情10
    数据还在计算中等待中,你可以做别的事情11
    数据还在计算中等待中,你可以做别的事情12
    数据还在计算中等待中,你可以做别的事情13
    数据还在计算中等待中,你可以做别的事情14
    打印结果是:xSharp:futureRunnable0:futureRunnable1:futureRunnable2:futureRunnable3:futureRunnable4:futureRunnable5:futureRunnable6:futureRunnable7:futureRunnable8:futureRunnable9

     接下来我使用Callable<V> 接口实现FutureTask,代码如下:

     1 package cn.com.futuretest;
     2  
     3 import java.util.concurrent.Callable;
     4 import java.util.concurrent.ExecutionException;
     5 import java.util.concurrent.ExecutorService;
     6 import java.util.concurrent.Executors;
     7 import java.util.concurrent.FutureTask;
     8  
     9 public class FutureCallable implements Callable<Result>{
    10  
    11     private Result result;  // 操作的数据,模拟一个计算需要很长时间的数据
    12      
    13     /* 初始化数据 */
    14     public FutureCallable(Result result) {
    15         this.result = result;
    16     }
    17  
    18     @Override
    19     public Result call() throws Exception {
    20         try {
    21             for (int i = 0;i < 10;i++){
    22                 Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
    23                 result.setData(result.getData() + ":" + "futureCallable" + i);
    24             }                      
    25         } catch (InterruptedException e) {
    26             e.printStackTrace();
    27         }
    28         return result;
    29     }
    30      
    31     public static void main(String[] args) {
    32         long start = System.currentTimeMillis();
    33         Result r = new Result("xSharp");// 构造测试数据
    34         FutureCallable callable = new FutureCallable(r);
    35         FutureTask<Result> task = new FutureTask<Result>(callable);
    36         // 构造固定大小为一个线程的线程池
    37         ExecutorService executorService = Executors.newFixedThreadPool(1);
    38         // 执行线程
    39         executorService.execute(task);
    40         System.out.println("执行完毕!");
    41         long curr01 = System.currentTimeMillis();
    42         System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
    43         try {
    44             for (int i = 0;i < 6;i++){
    45                 Thread.sleep(100);
    46                 System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
    47             }
    48         } catch (InterruptedException e) {
    49             e.printStackTrace();
    50         }
    51          
    52         try {
    53             System.out.println("打印结果是:" + task.get().getData());
    54             long end = System.currentTimeMillis();
    55             System.out.println("总耗时:" + (end - start) + "毫秒");
    56         } catch (InterruptedException e) {
    57             e.printStackTrace();
    58         } catch (ExecutionException e) {
    59             e.printStackTrace();
    60         }finally{
    61             System.exit(0);
    62         }
    63     }
    64  
    65 }
    View Code

    执行结果如下:

    执行完毕!
    任务提交后的耗时:6毫秒
    数据还在计算中等待中,你可以做别的事情0
    数据还在计算中等待中,你可以做别的事情1
    数据还在计算中等待中,你可以做别的事情2
    数据还在计算中等待中,你可以做别的事情3
    数据还在计算中等待中,你可以做别的事情4
    数据还在计算中等待中,你可以做别的事情5
    打印结果是:xSharp:futureCallable0:futureCallable1:futureCallable2:futureCallable3:futureCallable4:futureCallable5:futureCallable6:futureCallable7:futureCallable8:futureCallable9
    总耗时:1010毫秒

    这里我对代码做了一些调整,一个是加上了执行时间的统计,一个是我将干其他事情的程序执行时间变短,小于了线程本身执行的时间,这么做的目的是想和下面的程序对比,下面的代码当我执行线程后没有做其他的操作,而是直接获取线程执行的结果,具体代码如下:

    package cn.com.futuretest;
     
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.FutureTask;
     
    public class NioFutureCallable implements Callable<Result> {
         
        private Result result;  // 操作的数据,模拟一个计算需要很长时间的数据
         
        /* 初始化数据 */
        public NioFutureCallable(Result result) {
            this.result = result;
        }
     
        @Override
        public Result call() throws Exception {
            try {
                for (int i = 0;i < 10;i++){
                    Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
                    result.setData(result.getData() + ":" + "NioFutureCallable" + i);
                }                      
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }
     
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            Result r = new Result("xSharp");// 构造测试数据
            NioFutureCallable callable = new NioFutureCallable(r);
            FutureTask<Result> task = new FutureTask<Result>(callable);
            // 构造固定大小为一个线程的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            // 执行线程
            executorService.execute(task);
            System.out.println("执行完毕!");
            long curr01 = System.currentTimeMillis();
            System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
             
            /* 第一次获取返回数据 */
            try {
                System.out.println("第一次打印结果是:" + task.get().getData());
                long curr02 = System.currentTimeMillis();
                System.out.println("第一次获取结果耗时:" + (curr02 - start) + "毫秒");
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            } catch (ExecutionException e1) {
                e1.printStackTrace();
            }
             
            try {
                for (int i = 0;i < 10;i++){
                    Thread.sleep(100);
                    System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             
            try {
                System.out.println("第二次打印结果是:" + task.get().getData());
                long end = System.currentTimeMillis();
                System.out.println("总耗时:" + (end - start) + "毫秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }finally{
                System.exit(0);
            }
             
     
        }
     
    }
    View Code

    执行结果如下:

     1 执行完毕!
     2 任务提交后的耗时:7毫秒
     3 第一次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
     4 第一次获取结果耗时:1009毫秒
     5 数据还在计算中等待中,你可以做别的事情0
     6 数据还在计算中等待中,你可以做别的事情1
     7 数据还在计算中等待中,你可以做别的事情2
     8 数据还在计算中等待中,你可以做别的事情3
     9 数据还在计算中等待中,你可以做别的事情4
    10 数据还在计算中等待中,你可以做别的事情5
    11 数据还在计算中等待中,你可以做别的事情6
    12 数据还在计算中等待中,你可以做别的事情7
    13 数据还在计算中等待中,你可以做别的事情8
    14 数据还在计算中等待中,你可以做别的事情9
    15 第二次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
    16 总耗时:2012毫秒

    我们看到当我们直接获取结果时候,整个主线程都被阻塞了,直到结果返回后才会执行下面的后续操作,这也就是说如果计算还没结束,我们就想获取结果这样整个执行流程都将被阻塞,这点在我们合理使用Future模式时候很重要。

      除了使用FutureTask实现Future模式,我们还可以使用ExecutorService的submit方法直接返回Future对象,Future就和我前面设计的原生Future类似,当我们开始调用时候返回的是一个虚拟结果,其实实际的计算还没有结束,只有等待吗一会儿后结果才会真正的返回,代码如下:

    package cn.com.futuretest;
     
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
     
    public class RetFutureCallable implements Callable<Result>{
     
        private Result result;  // 操作的数据,模拟一个计算需要很长时间的数据
     
        public RetFutureCallable() {
            result = new Result("xSharp");
        }
     
        @Override
        public Result call() throws Exception {
            try {
                for (int i = 0;i < 10;i++){
                    Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
                    result.setData(result.getData() + ":" + "RetFutureCallable" + i);
                }                      
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }
         
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            RetFutureCallable callable = new RetFutureCallable();
            // 构造固定大小为一个线程的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            // 执行线程
            Future<Result> r = executorService.submit(callable);
            System.out.println("执行完毕!");
            long curr01 = System.currentTimeMillis();
            System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
            try {
                for (int i = 0;i < 6;i++){
                    Thread.sleep(100);
                    System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
             
            try {
                System.out.println("打印结果是:" + r.get().getData());
                long end = System.currentTimeMillis();
                System.out.println("总耗时:" + (end - start) + "毫秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }finally{
                System.exit(0);
            }
        }
         
         
    } 
    View Code

    执行结果如下:

     1 执行完毕!
     2 任务提交后的耗时:5毫秒
     3 数据还在计算中等待中,你可以做别的事情0
     4 数据还在计算中等待中,你可以做别的事情1
     5 数据还在计算中等待中,你可以做别的事情2
     6 数据还在计算中等待中,你可以做别的事情3
     7 数据还在计算中等待中,你可以做别的事情4
     8 数据还在计算中等待中,你可以做别的事情5
     9 打印结果是:xSharp:RetFutureCallable0:RetFutureCallable1:RetFutureCallable2:RetFutureCallable3:RetFutureCallable4:RetFutureCallable5:RetFutureCallable6:RetFutureCallable7:RetFutureCallable8:RetFutureCallable9
    10 总耗时:1006毫秒

    转自:http://www.cnblogs.com/sharpxiajun/p/5608663.html

  • 相关阅读:
    Delphi数据库处理
    delphi 递归的方法
    C#中StringBuilder用法以及和String的区别
    算法导论12.2节习题解答
    算法导论9.11习题解答(二叉树)
    算法导论10.15习题解答(deque实现源码)
    算法导论9.39习题解答(寻找中位数)
    算法导论84习题解答
    算法导论10.17习题解答(用两个队列实现一个栈)
    算法导论10.27习题解答(单链表逆转)
  • 原文地址:https://www.cnblogs.com/Sharley/p/5733763.html
Copyright © 2020-2023  润新知