zoukankan      html  css  js  c++  java
  • 线程返回值的方式介绍

    Java代码  收藏代码
    1. 在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。  
    2.    
    3. 现在Java终于有可返回值的任务(也可以叫做线程)了。  
    4.    
    5. 可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。  
    6.    
    7. 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。  
    8.    
    9. 下面是个很简单的例子:  
    10. Java代码    
    11. import java.util.concurrent.*;    
    12.     
    13. /**  
    14. * Java线程:有返回值的线程  
    15. *  
    16. * @author Administrator   
    17. */    
    18. public class Test {    
    19.         public static void main(String[] args) throws ExecutionException, InterruptedException {    
    20.                 //创建一个线程池    
    21.                 ExecutorService pool = Executors.newFixedThreadPool(2);    
    22.                 //创建两个有返回值的任务    
    23.                 Callable c1 = new MyCallable("A");    
    24.                 Callable c2 = new MyCallable("B");    
    25.                 //执行任务并获取Future对象    
    26.                 Future f1 = pool.submit(c1);    
    27.                 Future f2 = pool.submit(c2);    
    28.                 //从Future对象上获取任务的返回值,并输出到控制台    
    29.                 System.out.println(">>>"+f1.get().toString());    
    30.                 System.out.println(">>>"+f2.get().toString());    
    31.                 //关闭线程池    
    32.                 pool.shutdown();    
    33.         }    
    34. }    
    35.     
    36. class MyCallable implements Callable{    
    37.         private String oid;    
    38.     
    39.         MyCallable(String oid) {    
    40.                 this.oid = oid;    
    41.         }    
    42.     
    43.         @Override    
    44.         public Object call() throws Exception {    
    45.                 return oid+"任务返回的内容";    
    46.         }    
    47. }    
    48.  输出结果:  
    49. >>>A任务返回的内容   
    50. >>>B任务返回的内容   
    51.   
    52. Process finished with exit code 0  
    53.   
    54. 非常的简单,要深入了解还需要看Callable和Future接口的API啊。  
    55.   
    56. 第二种方法:  
    57. 从线程中返回数据和向线程传递数据类似。也可以通过类成员以及回调函数来返回数据。但类成员在返回数据和传递数据时有一些区别,下面让我们来看看它们区别在哪。  
    58.   一、通过类变量和方法返回数据  
    59.   使用这种方法返回数据需要在调用start方法后才能通过类变量或方法得到数据。让我们先来看看会得到什么结果。  
    60. Java代码    
    61. package mythread;    
    62.     
    63. public class MyThread extends Thread    
    64. {    
    65.     private String value1;    
    66.     private String value2;    
    67.     
    68.     public void run()    
    69.     {    
    70.         value1 = "通过成员变量返回数据";    
    71.         value2 = "通过成员方法返回数据";    
    72.     }    
    73.     public static void main(String[] args) throws Exception    
    74.     {    
    75.         MyThread thread = new MyThread();    
    76.         thread.start();    
    77.         System.out.println("value1:" + thread.value1);    
    78.         System.out.println("value2:" + thread.value2);    
    79.     }    
    80. }    
    81.  运行上面的代码有可能输出如下的结果:  
    82.   value1:null  
    83.   value2:null  
    84.   从上 面的运行结果看很不正常。在run方法中已经对value1和value2赋了值,而返回的却是null。发生这种情况的原因是调用start方法后就立 刻输出了value1和value2的值,而这里run方法还没有执行到为value1和value2赋值的语句。要避免这种情况的发生,就需要等run 方法执行完后才执行输出value1和value2的代码。因此,我们可以想到使用sleep方法将主线程进行延迟,如可以在 thread.start()后加一行如下的语句:sleep(1000);  
    85.   这样做可以使主线程延迟1秒后再往下执行,但这样做有一个问题,就是我们怎么知道要延迟多长时间。在这 个例子的run方法中只有两条赋值语句,而且只创建了一个线程,因此,延迟1秒已经足够,但如果run方法中的语句很复杂,这个时间就很难预测,因此,这 种方法并不稳定。  
    86.   我们的目的就是得到value1和value2的值,因此,只要判断value1和value2是否为null。如果它们都不为null时,就可以输出这两个值了。我们可以使用如下的代码来达到这个目的:  
    87.   while (thread.value1 == null || thread.value2 == null);  
    88.    使用上面的语句可以很稳定地避免这种情况发生,但这种方法太耗费系统资源。大家可以设想,如果run方法中的代码很复杂,value1和value2需 要很长时间才能被赋值,这样while循环就必须一直执行下去,直到value1和value2都不为空为止。因此,我们可以对上面的语句做如下的改进:  
    89.   while (thread.value1 == null || thread.value2 == null)  
    90.    sleep(100);  
    91.   在while循环中第判断一次value1和value2的值后休眠100毫秒,然后再判断这两个值。这样所占用的系统资源会小一些。  
    92.    上面的方法虽然可以很好地解决,但Java的线程模型为我们提供了更好的解决方案,这就是join方法。在前面已经讨论过,join的功能就是使用线程 从异步执行变成同步执行。当线程变成同步执行后,就和从普通的方法中得到返回数据没有什么区别了。因此,可以使用如下的代码更有效地解决这个问题:  
    93. Java代码    
    94. ...    
    95. thread.start();    
    96. thread.join();    
    97. ...    
    98.  在thread.join()执行完后,线程thread的run方法已经退出了,也就是说线程thread已经结束了。因此,在thread.join()后面可以放心大胆地使用MyThread类的任何资源来得到返回数据。   
    99.   
    100. 第三种:  
    101. 通过回调函数返回数据  
    102.   下面例子中通过Work类的process方法向线程中传递了计算结果,但同时,也通过process方法从线程中得到了三个随机数。因此,这种方法既可以向线程中传递数据,也可以从线程中获得数据。  
    103. Java代码    
    104. package mythread;    
    105.     
    106. class Data    
    107. {    
    108.     public int value = 0;    
    109. }    
    110. class Work    
    111. {    
    112.     public void process(Data data, Integer numbers)    
    113.     {    
    114.         for (int n : numbers)    
    115.         {    
    116.             data.value += n;    
    117.         }    
    118.     }    
    119. }    
    120. public class MyThread3 extends Thread    
    121. {    
    122.     private Work work;    
    123.     
    124.     public MyThread3(Work work)    
    125.     {    
    126.         this.work = work;    
    127.     }    
    128.     public void run()    
    129.     {    
    130.         java.util.Random random = new java.util.Random();    
    131.         Data data = new Data();    
    132.         int n1 = random.nextInt(1000);    
    133.         int n2 = random.nextInt(2000);    
    134.         int n3 = random.nextInt(3000);    
    135.         work.process(data, n1, n2, n3);   // 使用回调函数    
    136.         System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+"    
    137.                 + String.valueOf(n3) + "=" + data.value);    
    138.     }    
    139.     public static void main(String[] args)    
    140.     {    
    141.         Thread thread = new MyThread3(new Work());    
    142.         thread.start();    
    143.     }    
    144. }    
    145.  在上面代码 中的 process方法被称为回调函数。从本质上说,回调函数就是事件函数。在 Windows API中常使用回调函数和调用 API的程序之间进行数据交互。因此,调用回调函数的过程就是最原始的引发事件的过程。在这个例子中调用了 process方法来获得数据也就相当于在 run方法中引发了一个事件  
  • 相关阅读:
    WebSocket简单使用
    viewport 的基本原理以及使用
    Markdown基本语法总结
    emmet 工具的基本使用,总结
    在idea中把项目上传到GitHub库中
    Git Bash命令汇总
    用github创建自己的存储库并把文件推送到远程库中
    之前编写的Symfony教程已经可以观看了
    Symfony路由配置教程已开课
    Symfony原创视频教程
  • 原文地址:https://www.cnblogs.com/leo3689/p/4408954.html
Copyright © 2011-2022 走看看