我们知道,在java中线程实现有两种方法,第一是继承Thread,第二是实现Runnable接口。无论哪个,都要重写run()方法,由于是重写,而且run()的定义:public abstract void run(),显而易见,这个函数式、是没有返回值的。那么怎样获取线程执行后的结果?
让我们来看一个例子
package com.lu.util;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
/**
* 网络通用类
*
* @author lu
*
*/
public class NetWorkUtil {
/**
* 从给定的url页面获取页面内容
*
* @param urlAddr
* @return
* @throws Exception
*/
public String getPageContent(String urlAddr) throws Exception {
HttpURLConnection httpURLConn = null;
InputStream input = null;
Scanner scanner = null;
try {
URL url = new URL(urlAddr);
httpURLConn = (HttpURLConnection) url.openConnection();
httpURLConn.setDoInput(true);
httpURLConn.setRequestMethod("GET");
httpURLConn.connect();
input = httpURLConn.getInputStream();
scanner = new Scanner(input);
StringBuffer strBuf = new StringBuffer();
while (scanner.hasNextLine()) {
strBuf.append(scanner.nextLine());
}
return strBuf.toString();
} finally {
if (null != scanner) {
scanner.close();
}
if (null != input) {
input.close();
}
if (null != httpURLConn) {
httpURLConn.disconnect();
}
}
}
}
//如果不了解URL、URLConnection,大家可以跳过不看,只需知道,如果调用该方法,就是获取给定url的页面内容即可。
package com.lu.callback;
import com.lu.util.NetWorkUtil;
/**
* 该线程负责获取给定url页面的内容
*
* @author lu
*
*/
public class GetPageContentThread implements Runnable {
NetWorkUtil util = new NetWorkUtil();
public String pageContent;
public String urlAddr;
public GetPageContentThread(String urlAddr) {
this.urlAddr = urlAddr;
}
@Override
public void run() {
try {
//获取页面内容
pageContent = util.getPageContent(urlAddr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.lu.callback;
public class Client {
public static void main(String[] args) {
String url = "http://www.baidu.com";
GetPageContentThreadtr = new GetPageContentThread(url);
new Thread(tr).start();
System.out.println(tr.pageContent.length());
}
}
客户端开启线程并启动,一次来获取百度首页的内容。但是运行一下会发现程序抛出java.lang.NullPointerException异常。为什么呢?大家不要忘了这是在多线程环境下。如果执行System.out.println(tr.pageContent.length());的时候,获取页面的线程还没有获得时间片去执行,那么pageContent肯定为null,那么抛出异常就是毫无疑问了。
如果把客户端代码改成这样,那么问题就解决了。
package com.lu.callback;
public class Client {
public static void main(String[] args) {
String url = "http://www.baidu.com";
ThreadResult tr = new ThreadResult(url);
new Thread(tr).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tr.pageContent.length());
}
}
主线程开启另一个线程后,休眠1秒钟,让给其他线程去执行。等主线程获得时间片去执行时,页面内容已经获得。但是存在一个更大的问题,休眠的时间怎么确定,如果休眠1秒,获取页面的时间为800毫秒,就白白浪费了CPU的资源。所以肯定有更好的办法,那就是回调。下面直接看代码。
package com.lu.callback;
/**
* 该接口定义了process方法,改方法为回调调用的方法。
* @author lu
*
*/
public interface Callback {
public void process(Object... objects);
}
package com.lu.callback;
/**
* 改类继承了Callback接口并实现了process()这个回调方法,用于回调并处理
* @author lu
*
*/
public class GetPageContentCallBack implements Callback {
public String urlAddr = null;
public String pageContent = null;
public GetPageContentCallBack(String urlAddr) {
this.urlAddr = urlAddr;
}
@Override
public void process(Object... objects) {
if (objects != null && objects.length >= 1) {
pageContent = (String) objects[0];
}
System.out.println(pageContent.length());
}
/**
* 该方法创建了获取页面内容的线程,并把自身当做参数传给改线程。
*/
public void getPageContent() {
GetPageContentThread tr = new GetPageContentThread(urlAddr);
tr.setCallback(this);
new Thread(tr).start();
}
}
package com.lu.callback;
import com.lu.util.NetWorkUtil;
/**
* 该线程负责获取给定url页面的内容
*
* @author lu
*
*/
public class GetPageContentThread implements Runnable {
private NetWorkUtil util = new NetWorkUtil();
public String pageContent;
public String urlAddr;
private Callback callback = null;
public GetPageContentThread(String urlAddr) {
this.urlAddr = urlAddr;
}
@Override
public void run() {
try {
// 获取页面内容
pageContent = util.getPageContent(urlAddr);
} catch (Exception e) {
e.printStackTrace();
}
//这条语句是关键所在,当获取页面之后,调用处理方法。
callback.process(pageContent);
}
public void setCallback(Callback callback) {
this.callback = callback;
}
}
package com.lu.callback;
public class Client {
public static void main(String[] args) {
String url = "http://www.baidu.com";
GetPageContentCallBack getPageContentCallBack = new GetPageContentCallBack(url);
//调用获取页面方法
getPageContentCallBack.getPageContent();
}
}
首先客户端调用getPageContent()方法,该方法创建获取页面线程并把自身传递给该线程,即将自身的引用给了改线程。获取页面的线程执行run()方法获取页面,等获取成功后,调用callback的process()方法,并把结果传递给他。就这样得到了线程执行的结果并做出处理。
现在如果有多个对象关注该线程执行后的结果,那改怎么去通知呢?将其他想要关注该线程结果的对象都添加到该线程对象中就可以了,观察者... 主题... blalalala.... 好像是某个设计模式。
参考文献:java网络编程
小生不才,学术不精,欢迎大家批评指正