java中获取另一个线程中的信息

简介: 在进行多线程编程中,比较重要也是比较困难的一个操作就是如何获取线程中的信息。大多数人会采取比较常见的一种方法就是将线程中要返回的结果存储在一个字段中,然后再提供一个获取方法将这个字段的内容返回给该方法的调用者。如以下的ReturnThreadInfo类:package threadtest1;public class ReturnThreadInfo extends Thread {
在进行多线程编程中,比较重要也是比较困难的一个操作就是如何获取线程中的信息。大多数人会采取比较常见的一种方法就是将线程中要返回的结果存储在一个字段中,然后再提供一个获取方法将这个字段的内容返回给该方法的调用者。如以下的ReturnThreadInfo类:
package threadtest1;

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;

public class 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;

public class 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{
   
    /**
     * @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;
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处理事件的方法,这么做可以使得回调更灵活,可以处理涉及更多线程、对象和类的情况。稍后会给出这种模仿事件处理模型的回调的实现方法。
目录
相关文章
|
6天前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
6天前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
3天前
|
存储 缓存 安全
深度剖析Java HashMap:源码分析、线程安全与最佳实践
深度剖析Java HashMap:源码分析、线程安全与最佳实践
|
5天前
|
数据采集 机器学习/深度学习 前端开发
Java爬虫中的数据清洗:去除无效信息的技巧
Java爬虫中的数据清洗:去除无效信息的技巧
|
5天前
|
缓存 前端开发 JavaScript
一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
【8月更文挑战第11天】一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
13 0
一篇文章助你搞懂java中的线程概念!纯干货,快收藏!
|
5天前
|
存储 Java
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
这篇文章通过Java代码示例展示了如何实现哈希表,包括定义结点类、链表类、数组存储多条链表,并使用简单的散列函数处理冲突,以及如何利用哈希表存储和查询学生信息。
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
|
3天前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
4天前
|
Java 程序员 调度
深入浅出Java多线程编程
Java作为一门成熟的编程语言,在多线程编程方面提供了丰富的支持。本文将通过浅显易懂的语言和实例,带领读者了解Java多线程的基本概念、创建方法以及常见同步工具的使用,旨在帮助初学者快速入门并掌握Java多线程编程的基础知识。
4 0
|
5天前
|
Java
java中获取当前执行线程的名称
这篇文章介绍了两种在Java中获取当前执行线程名称的方法:使用`Thread`类的`getName`方法直接获取本线程的名称,以及使用`Thread.currentThread()`方法获取当前执行对象的引用再调用`getName`方法。
|
5天前
|
Java 测试技术
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
10 0