`
nlslzf
  • 浏览: 1045016 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

获取java线程中信息的两种方法

    博客分类:
  • java
阅读更多
http://www.cn-java.com/www1/?action-viewnews-itemid-4527
在进行多线程编程中,比较重要也是比较困难的一个操作就是如何获取线程中的信息。大多数人会采取比较常见的一种方法就是将线程中要返回的结果存储在一个字段中,然后再提供一个获取方法将这个字段的内容返回给该方法的调用者。如以下的ReturnThreadInfo类:
package threadtest1;
/**
 *
 * @author shi mingxiang
 */
public class ReturnThreadInfo extends Thread {
    private String str; 
    
    public ReturnThreadInfo() {
        this.str = "Hello";
    }
    
    public void run(){
            this.str = "Hello World!";
    }
    
    public String getThreadInfo(){
        return this.str;
    }
}

  大家可以看到该类是一个线程类并含有一个初始值为"Hello"的字段str以及一个可以返回str值的方法:getThreadInfo(),而且当这个线程启动后str会被赋于新值:"Hello World!"。现在我想在另外一个类中启动ReturnThreadInfo线程,并通过getThreadInfo()方法获取值为"Hello World!"的变量并打印输出到控制台中。以下给出一个实现该功能的Main类:


package threadtest1;
/**
 *
 * @author shi mingxiang
 */
public class Main{
    
    public Main() {
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo(); 
        returnThreadInfo.start();  //创建并启动ReturnThreadInfo线程
        System.out.println(returnThreadInfo.getThreadInfo());  //获取并输出returnThreadInfo对象的str的值     
    }
    
}

  以上是一个多数熟悉单线程编程的人在第一反应下给出的实现方法。但是该类在运行的时候输出的结果却不是期望的"Hello World!"而是"Hello",这是由于线程的竞争条件导致的(由于ReturnThreadInfo线程和Main线程的优先级都为5,所以在很大几率上ReturnThreadInfo线程的run()方法还没有运行,Main类就已经运行System.out.println(returnThreadInfo.getThreadInfo());将"Hello"输出了。具体的原理可以参见另一篇文章:"java多线程的几点误区")。有的人可能会立即想到把ReturnThreadInfo线程的优先级设高些(比如最大的10)就可以returnThreadInfo线程的run()方法先运行完,然后Main类的System.out.println(returnThreadInfo.getThreadInfo())再运行,这样输出的结就一定是期望的"Hello World!"了。这种通过调整线程优先级的方法固然可以在某种程度上解决该问题,但是线程争用CPU运行时间的原理却决不仅仅只是优先级高低的原因(优先级高的线程并不意味着一定比优先级低的线程先运行,只是几率要更大一些)。你并不希望ReturnThreadInfo线程9999次都比Main先运行,却在最关键的一次在Main之后再运行。因此下面给出两种比较常见的获取线程信息的方法:
一、轮询
  比较常见的一种解决方案是,让线程类获取方法在结果字段设置之前返回一个标志值。然后主线程定时询问获取方法,看是否返回了标志之外的值。以下给出了具体的实现方法,该方法不断测试str的值是否为"Hello",如果不为"Hello"才打印输出它。例如:


package threadtest1;
/**
 *
 * @author shi mingxiang
 */
public class Main{
    
    public Main() {
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo(); 
        returnThreadInfo.start();  //创建并启动ReturnThreadInfo线程


        while(true){
            String str = returnThreadInfo.getThreadInfo();
            if(!str.equals("Hello")){
                 System.out.println(returnThreadInfo.getThreadInfo());
                 break;
            }
        }     
    } 
}

  这种方案虽然能起到作用,但是它做了大量不需要做的工作。事实上,还有一种更简单有效的方法来解决这个问题。


二、回调
  轮询方法最大的特点是主类Main不断询问线程类是否结束,这实际上大量浪费了运行时间,特别是当线程特别多的时候。因此如果反过来在线程结束时,由线程自己告诉主类Main线程已经结束,然后Main再获取并输出str的值,这样就避免了轮询方法所带来的不必要的系统开销问题。
  在具体的实现过程中,线程可以在结束时通过调用主类中的一个方法来实现告知功能,这种方法叫做回调。这样主类Main就可以在等待线程结束时休息,也就不会占用运行线程的时间。下面是修改后的Main类:
public class Main{
    
    public Main() {
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo();
        returnThreadInfo.start();
    }
    
    public static void receiveStr(String str){
        System.out.println(str);
    }
}

  相比于前面,我们在Main类中添加了一个静态方法receiveStr(String str),该方法是供线程结束之前调用,通过参数str将要返回的线程信息返回给Main类并输出显示出来。下面是修改后的ReturnThreadInfo类,该类在线程结束前回调了Main.receiveStr方法,通知线程已结束。


package threadtest1;
/**
 *
 * @author shi mingxiang
 */
public class ReturnThreadInfo extends Thread {
    private String str; 
    
    public ReturnThreadInfo() {
        this.str = "Hello";
    }
    
    public void run(){
            this.str = "Hello World!";
     Main.receiveStr(str); //回调receiveStr方法
    }
}



  如果有很多个对象关心线程的返回的信息,线程可以保存一个回调对象列表。某个对象可以通过已经定义的一个对象将自己添加到列表中,表示自己对这些信息的关注。如果有多个类的实例关心这些信息,也可以定义一个interface,在interface中声名回调方法,然后这些类都实现这个接口。其实这是典型的java处理事件的方法,这么做可以使得回调更灵活,可以处理涉及更多线程、对象和类的情况。稍后会给出这种模仿事件处理模型的回调的实现方法。
分享到:
评论

相关推荐

    java多线程的两种实现

    在Java中,有两种主要的实现多线程的方式:通过`Thread`类和通过实现`Runnable`接口。 1. **通过`Thread`类实现多线程** 当我们创建一个`Thread`类的子类时,可以重写`run()`方法来定义线程执行的任务。例如: ``...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    关于线程(java)两天的课件

    在Java中,有两种主要的方式来创建线程:继承Thread类并重写run()方法,或者实现Runnable接口并将其实例传递给Thread构造函数。继承Thread类的方法直接扩展Thread类,而实现Runnable接口则更适合多线程资源共享的...

    java中创建线程两个方法及区别

    Java提供了多种创建线程的方式,其中最常见的是通过继承`Thread`类和实现`Runnable`接口这两种方法。下面将详细阐述这两种创建线程的方法及其区别。 ### 一、通过继承`Thread`类创建线程 当一个类继承了`Thread`类...

    Java线程使用教程

    2. **线程的创建**:Java提供了两种创建线程的方式,一是继承`Thread`类并重写`run()`方法,二是实现`Runnable`接口并提供`run()`方法,然后通过`Thread`对象启动。 3. **线程的启动**:通过`start()`方法启动线程...

    Java线程模块Java线程之秒表

    在Java中,有两种方式来创建线程:继承Thread类或实现Runnable接口。如果你选择继承Thread,你需要重写`run()`方法,而在实现Runnable接口时,你需要提供一个实现了`run()`方法的类,并将其实例传递给Thread对象。...

    Java 模拟线程并发

    首先,Java中创建线程主要有两种方法。一种是通过继承Thread类,重写其run()方法,然后实例化并调用start()方法启动线程。例如: ```java class MyThread extends Thread { @Override public void run() { // ...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而...在"线程池.rar"和"线程实例"这两个文件中,你可以找到关于这些概念的具体示例代码,通过学习和实践,可以深入理解Java线程的运用和管理。

    Java 多线程 PPT

    创建Java线程主要有两种方式: 1. 继承Thread类:创建Thread的子类并重写run()方法,然后通过调用start()方法启动线程。 2. 实现Runnable接口:创建一个实现了Runnable接口的类,实现run()方法,然后将该类的实例...

    java线程文档大全

    2. **线程创建**:Java提供两种方式创建线程,一是直接继承Thread类并重写run()方法,二是实现Runnable接口并定义run()方法,然后通过Thread类的构造函数将Runnable对象传递进去。 3. **线程交互**:线程之间的通信...

    java线程入门级书籍

    Java线程是Java编程中非常重要的一部分,它使得程序能够处理并发任务,提高程序的效率和响应能力。通过合理地设计和使用线程,开发者可以构建出更高效、更健壮的应用程序。以上介绍的是Java线程的基础知识,更多高级...

    java线程实战手册

    1. **线程的创建**:在Java中,有两种主要的线程创建方式——继承Thread类和实现Runnable接口。每种方法各有优缺点,需要根据实际需求选择。 2. **线程的启动与生命周期**:通过调用start()方法启动线程,线程将...

    Java多线程初学者指南(7):向线程传递数据的三种方法.docx )

    上面讨论的两种向线程中传递数据的方法都是 main 方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据。例如,以下代码演示了如何使用回...

    java线程状态转换图

    * 线程的实现有两种方式,一是继承 Thread 类,二是实现 Runnable 接口,但不管怎样,当我们 new 了这个对象后,线程就进入了初始状态。 * 在初始状态,线程对象还没有启动,尚未开始执行。 可运行状态(Runnable)...

    Java线程学习和总结

    Java线程的创建主要有两种方式:继承`Thread`类和实现`Runnable`接口。继承`Thread`类时,重写`run()`方法并在其中编写线程代码;实现`Runnable`接口则需要将线程代码放入`run()`方法中,并通过`Thread`类的构造函数...

    eclipse项目java线程实例

    Java线程是多任务编程的核心概念,特别是在大型的、复杂的软件系统中,如Eclipse这样的集成开发环境(IDE)中的项目。在这个"Eclipse项目java线程实例"中,我们可以深入理解并实践Java线程的创建、管理和同步机制。 ...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    java 多线程示例

    Java提供了两种创建线程的方式:继承`Thread`类和实现`Runnable`接口。在`lec22`中,可能会包含这两个方法的示例。继承`Thread`类时,重写`run()`方法,并直接通过`start()`启动新线程。而实现`Runnable`接口则需要...

    JNI层创建的线程中回调java方法

    在JNI中回调Java方法主要有两种方式: 1. **使用JNIEnv**:JNIEnv是一个指向一系列函数指针的结构体,它提供了在本地代码中调用Java方法的接口。当JNI函数被Java方法调用时,JNIEnv会被自动传入。但是,如果在本地...

    java线程与并发编程实践

    Java线程与并发编程实践是Java开发者必备的技能之一,特别是在多核处理器和高并发应用环境中,有效地管理和利用线程能极大地提升程序的性能。本书《java线程与并发实践编程》由Jeff Friesen撰写,2017年2月出版,...

Global site tag (gtag.js) - Google Analytics