`
xiaoboss
  • 浏览: 643979 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

从线程返回数据的几种方法

    博客分类:
  • java
阅读更多
在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。
 
现在Java终于有可返回值的任务(也可以叫做线程)了。
 
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。
 
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
 
下面是个很简单的例子:
import java.util.concurrent.*;

/**
* Java线程:有返回值的线程
*
* @author Administrator 
*/
public class Test {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建两个有返回值的任务
                Callable c1 = new MyCallable("A");
                Callable c2 = new MyCallable("B");
                //执行任务并获取Future对象
                Future f1 = pool.submit(c1);
                Future f2 = pool.submit(c2);
                //从Future对象上获取任务的返回值,并输出到控制台
                System.out.println(">>>"+f1.get().toString());
                System.out.println(">>>"+f2.get().toString());
                //关闭线程池
                pool.shutdown();
        }
}

class MyCallable implements Callable{
        private String oid;

        MyCallable(String oid) {
                this.oid = oid;
        }

        @Override
        public Object call() throws Exception {
                return oid+"任务返回的内容";
        }
}
 输出结果:
>>>A任务返回的内容
>>>B任务返回的内容

Process finished with exit code 0

非常的简单,要深入了解还需要看Callable和Future接口的API啊。

第二种方法:
从线程中返回数据和向线程传递数据类似。也可以通过类成员以及回调函数来返回数据。但类成员在返回数据和传递数据时有一些区别,下面让我们来看看它们区别在哪。

  一、通过类变量和方法返回数据

  使用这种方法返回数据需要在调用start方法后才能通过类变量或方法得到数据。让我们先来看看会得到什么结果。

package mythread;

public class MyThread extends Thread
{
    private String value1;
    private String value2;

    public void run()
    {
        value1 = "通过成员变量返回数据";
        value2 = "通过成员方法返回数据";
    }
    public static void main(String[] args) throws Exception
    {
        MyThread thread = new MyThread();
        thread.start();
        System.out.println("value1:" + thread.value1);
        System.out.println("value2:" + thread.value2);
    }
}
 运行上面的代码有可能输出如下的结果:

  value1:null

  value2:null

  从上 面的运行结果看很不正常。在run方法中已经对value1和value2赋了值,而返回的却是null。发生这种情况的原因是调用start方法后就立 刻输出了value1和value2的值,而这里run方法还没有执行到为value1和value2赋值的语句。要避免这种情况的发生,就需要等run 方法执行完后才执行输出value1和value2的代码。因此,我们可以想到使用sleep方法将主线程进行延迟,如可以在 thread.start()后加一行如下的语句:sleep(1000);

  这样做可以使主线程延迟1秒后再往下执行,但这样做有一个问题,就是我们怎么知道要延迟多长时间。在这 个例子的run方法中只有两条赋值语句,而且只创建了一个线程,因此,延迟1秒已经足够,但如果run方法中的语句很复杂,这个时间就很难预测,因此,这 种方法并不稳定。

  我们的目的就是得到value1和value2的值,因此,只要判断value1和value2是否为null。如果它们都不为null时,就可以输出这两个值了。我们可以使用如下的代码来达到这个目的:

  while (thread.value1 == null || thread.value2 == null);

   使用上面的语句可以很稳定地避免这种情况发生,但这种方法太耗费系统资源。大家可以设想,如果run方法中的代码很复杂,value1和value2需 要很长时间才能被赋值,这样while循环就必须一直执行下去,直到value1和value2都不为空为止。因此,我们可以对上面的语句做如下的改进:

  while (thread.value1 == null || thread.value2 == null)

   sleep(100);

  在while循环中第判断一次value1和value2的值后休眠100毫秒,然后再判断这两个值。这样所占用的系统资源会小一些。

   上面的方法虽然可以很好地解决,但Java的线程模型为我们提供了更好的解决方案,这就是join方法。在前面已经讨论过,join的功能就是使用线程 从异步执行变成同步执行。当线程变成同步执行后,就和从普通的方法中得到返回数据没有什么区别了。因此,可以使用如下的代码更有效地解决这个问题:

...
thread.start();
thread.join();
...
 在thread.join()执行完后,线程thread的run方法已经退出了,也就是说线程thread已经结束了。因此,在thread.join()后面可以放心大胆地使用MyThread类的任何资源来得到返回数据。 

第三种:
通过回调函数返回数据

  下面例子中通过Work类的process方法向线程中传递了计算结果,但同时,也通过process方法从线程中得到了三个随机数。因此,这种方法既可以向线程中传递数据,也可以从线程中获得数据。

package mythread;

class Data
{
    public int value = 0;
}
class Work
{
    public void process(Data data, Integer numbers)
    {
        for (int n : numbers)
        {
            data.value += n;
        }
    }
}
public class MyThread3 extends Thread
{
    private Work work;

    public MyThread3(Work work)
    {
        this.work = work;
    }
    public void run()
    {
        java.util.Random random = new java.util.Random();
        Data data = new Data();
        int n1 = random.nextInt(1000);
        int n2 = random.nextInt(2000);
        int n3 = random.nextInt(3000);
        work.process(data, n1, n2, n3);   // 使用回调函数
        System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+"
                + String.valueOf(n3) + "=" + data.value);
    }
    public static void main(String[] args)
    {
        Thread thread = new MyThread3(new Work());
        thread.start();
    }
}
 在上面代码 中的 process方法被称为回调函数。从本质上说,回调函数就是事件函数。在 Windows API中常使用回调函数和调用 API的程序之间进行数据交互。因此,调用回调函数的过程就是最原始的引发事件的过程。在这个例子中调用了 process方法来获得数据也就相当于在 run方法中引发了一个事件。
分享到:
评论

相关推荐

    Delphi多线程编程之三 同步读写全局数据

    三、还有一种用信号量对象来管理线程同步的,它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。有点复杂,想不到在哪可以用,现在就不研究论了。 unit Tst_...

    linux系统编程之线程.zip

    在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是...

    java笔试题大集合及答案(另附各大公司笔试题)

    60、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法 反对使用...

    QTcpThreadPoolServiceTest

    试验中,设计了一个设置为CPU核心数的线程池,这个线程池可以异步接受N个数据生产者传入的数据,均衡的分配处理任务,处理后的数据返回给某1个或者几个消费者。有两种均衡方法。 一种是生产者粒度的均衡。同一个...

    8-14 B站 C++安全开发面经 《腾讯》.docx

    6.注入:DLL注入有哪几种方法? 7.如何防止远程线程注入? 8.详细说一下 inline HOOK的原理 9.如果你在inline HOOK时候,正好有代码执行到相关位置怎么办?解决办法? 10.已知一个dll里面的很多函数(API)都被...

    深入理解Netty线程模型

    从这篇文章中,大家可以学习到如下知识:什么是I/O多路复用Reactor三种线程模型Netty线程模型NioEventLoop源码分析JDKepollbug学习I/O多路复用之前,我们先来了解如下几个概念:阻塞I/O:客户端从socket中读取数据或...

    基于并发服务器几种实现方法(总结)

    今天主题是实现并发服务器,实现方法有多种版本,先从简单的单进程代码实现到多进程,多线程的实现,最终引入一些高级模块来实现并发TCP服务器。 说到TCP,想起吐槽大会有个段子提到三次握手,也只有程序猿(媛)能get...

    pthread_doc

    在 Posix 线程编程中,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和...

    超级有影响力霸气的Java面试题大全文档

    此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),...

    java 面试题 总结

    此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),...

    java面试题

    多线程几种实现方法,同步? 答:多线程有两种实现方法,一种是继承Thread类或者实现Runnable接口。同步就是在方法返回类型后面加上synchronized。 c#中的委托,事件是不是委托? 答:委托就是将方法作为一个参数...

    java基础题 很全面

    54. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 13 55. java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类? 14 56....

    android 面试2

    避免ANR:Android应用程序通常运行在一个单独的线程里面,称谓主线程,所以在主线程里面少做一些耗时长的程序,而是利用子线程来操作一些繁琐的事情,用Handler来把子线程处理的消息返回给主线程 22、简要解释一下...

    JAVA面试题最全集

    76.EJB有哪几种?区别是什么? 77.JavaBean与EJB有什么区别? 78.软件开发生命周期有哪几个阶段? 79.软件开发有哪些因素? 80.软件开发中如何进行版本控制? 81.UML中,类视图如何表示类中的继承与聚合? 82.客户端...

    千方百计笔试题大全

    70、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 17 71、启动一个线程是用run()还是start()? 17 72、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 18 73...

    java面试宝典

    70、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 17 71、启动一个线程是用run()还是start()? 17 72、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 18 73...

    GitHub 上标星 115k+ 的 Java 教程.pdf

    Object 中有几个重要的方法: * equals() 方法用于比较两个对象是否相等 * hashCode() 方法用于返回对象的哈希码 * toString() 方法用于返回对象的字符串表示 * clone() 方法用于创建对象的副本 四、基本运算符 ...

    C#全能速查宝典

    1.4.16 GetDayOfWeek方法——返回星期几 59 1.4.17 GetDayOfYear方法——返回第几天 60 1.4.18 GetDaysInMonth方法——返回指定月份中的天数 60 1.4.19 GetDaysInYear方法——返回指定年份中的天数 61 1.4.20 ...

Global site tag (gtag.js) - Google Analytics