副标题#e#
在举办多线程编程中,较量重要也是较量坚苦的一个操纵就是如何获取线程中的信息。大大都人会采纳较量常见的一种要领就是将线程中要返回的功效存储在一个字段中,然后再提供一个获取要领将这个字段的内容返回给该要领的挪用者。如以下的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之后再运行。因此下面给出两种较量常见的获取线程信息的要领:
#p#副标题#e#
一、轮询
较量常见的一种办理方案是,让线程类获取要领在功效字段配置之前返回一个符号值。然后主线程按时询问获取要领,看是否返回了符号之外的值。以下给出了详细的实现要领,该要领不绝测试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的值,这样就制止了轮询要领所带来的不须要的系统开销问题。
#p#分页标题#e#
在详细的实现进程中,线程可以在竣事时通过挪用主类中的一个要领来实现奉告成果,这种要领叫做回调。这样主类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处理惩罚事件的要领,这么做可以使得回调更机动,可以处理惩罚涉及更多线程、工具和类的环境。稍后会给出这种仿照事件处理惩罚模子的回调的实现要领。