`
xiaoboss
  • 浏览: 650855 次
  • 性别: 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方法中引发了一个事件。
分享到:
评论

相关推荐

    Java多线程批量数据导入的方法详解.rar

    总结来说,Java多线程批量数据导入是一种高效处理大数据的方法。正确地设计和实施多线程策略,结合合适的并发控制和错误处理机制,能够有效提升系统的并发处理能力和性能。通过不断实践和优化,我们可以构建出更加...

    大数据量多线程执行分页查询

    3. **分页查询**:分页是一种优化大数据量查询的技术,它将结果集分割成多个小块(页面),每次只返回用户请求的一部分数据。这样不仅可以减轻服务器压力,还能减少网络传输的负担,提高响应速度。 4. **C#线程**:...

    QT多线程moveToThread使用方式

    可以使用`quit()`方法来停止事件循环,然后等待`wait()`方法返回,确保线程已退出。 在提供的`TestMoveToThread`文件中,可能包含了具体的示例代码,演示了如何创建和使用`moveToThread`方法。这个例子可能包括两个...

    delphi多线程传递参数及同步二

    Delphi 提供了几种同步机制,如 TEvent, TMutex, TCriticalSection 等。在这里,我们使用 TEvent 来同步主线程和工作线程。工作线程完成任务后,通过 SetEvent 触发事件,主线程则通过 WaitFor 监听该事件,确保在...

    java多线程分页查询

    因此,采用一种能够预先查询数据,并且能够快速响应后续请求的方法显得尤为重要。 #### 二、Java多线程分页查询原理及实现 ##### 1. 分页查询基础概念 分页查询是指在查询数据时,将数据分成多个页面展示,而不是...

    多线程编程之三——线程间通讯

    下面将详细讨论几种常见的线程间通信方式。 1. 使用全局变量进行通信 全局变量是所有线程都能访问的内存区域,因此可以用来传递简单的数据。为了确保多线程环境下的正确性,通常会使用`volatile`关键字修饰全局变量...

    C#多线程处理实例

    在调用 Thread 类的 Start 方法之前,会一直保持在 Unstarted 状态,调用方法之后,就会进入 Started 状态, 并立即将程序的控制权返回调用程序(点了线程调用后,可以立即去干别的事)。 四、线程优先级 每个线程...

    c#跨线程间控件调用

    本文将详细介绍几种常见的跨线程调用控件的方法,并解释其背后的原理。 #### InvokeRequired 属性 在Windows窗体控件中,有一个重要的属性 `InvokeRequired`,用于检查当前线程是否是创建该控件的线程。当该属性...

    易语言正确退出线程

    易语言是一种中文编程环境,它提供了方便的线程操作接口。本篇将详细探讨如何在易语言中实现“正确退出线程”这一重要知识点。 首先,理解线程的基本概念是至关重要的。线程是程序中的执行路径,一个进程可以包含多...

    线程及多线程的详细讲解

    线程在其生命周期中会经历以下几种状态:新建、就绪、运行、阻塞和死亡。 1. **新建**:创建了一个新的线程对象。 2. **就绪**:调用`start()`方法后,线程处于可运行状态,等待CPU时间片。 3. **运行**:线程获得...

    Facebook公开的多线程管理方法

    线程池是一种线程使用模式,它预先创建了一组可重用的线程,而不是每当需要时就创建新线程。这种设计可以避免频繁创建和销毁线程带来的性能开销,同时能够更好地管理和控制系统的资源。Facebook的线程池可能包含了...

    linux下C语言多线程编程实例

    3. `pthread_exit()`: 结束当前线程并返回一个状态码。 4. `pthread_self()`: 获取当前线程ID。 5. `pthread_equal()`: 比较两个线程ID是否相等。 6. 同步机制:`pthread_mutex_t`互斥量,用于保护临界区;`pthread_...

    易语言线程互斥对象解决

    线程池是一种线程使用模式,它预先创建一组线程,当需要执行任务时,从线程池中取出空闲线程执行任务,完成后线程返回线程池,而不是直接销毁。线程池可以有效地减少线程创建和销毁的开销,提高系统效率。在易语言...

    简单多线程程序

    线程池预先创建一定数量的线程,当有任务需要执行时,从池中获取空闲线程,任务完成后线程返回到池中等待下一次使用。 6. **线程通信**:线程间通信是多线程编程中的另一个重要方面,主要目的是协调线程间的操作。...

    多线程thread线程的理解

    Java中的`ExecutorService`和`ThreadPoolExecutor`就是线程池的实现,它们允许预先创建一定数量的线程,然后根据需要从池中获取和返回线程。 7. **并发与并行**:并发是指多个任务在一段时间内交替执行,而并行则...

    vc++工作线程封装类

    在VC++编程环境中,多线程技术是一种常用的方法来实现程序的并发执行,提高系统效率。本主题将详细探讨如何使用VC++中的`::CreateThread`函数进行工作线程的封装,以及相关的重要知识点。 首先,`::CreateThread`是...

Global site tag (gtag.js) - Google Analytics