习惯了单线程模型的程序员在转向多线程时,西南掌握的一点就是如何从线程返回信息。因为Thread的run方法和start方法都不会返回信息。
使用存取方法返回结果的线程。
public class ReturnDigest extends Thread{
private String filename;
private byte[] digest;
public ReturnDigest(String filename){
//在构造函数中传入文件名
this.filename = filename;
}
@Override
public void run() {
try {
FileInputStream in = new FileInputStream(filename);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
digest = sha.digest();//任务完成后将结果传给类变量
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//给类变量设置get方法,以便在该线程之外拿到线程的执行结果
public byte[] getDigest(){
return digest;
}
}
//主程序
public static void main(String[] args){
for(String filename:args){
//将文件名传给该线程,该线程只负责相应文件的加密工作
ReturnDigest dr = new ReturnDigest("d:\\ttt.txt");
dr.start();
//显示结果
StringBuilder result = new StringBuilder("d:\\ttt.txt");
result.append(":");
//通过线程的get方法得到执行结果
byte[] digest = dr.getDigest(); result.append(DatatypeConverter.printHexBinary(digest));
System.out.println(result);
}
}
执行以上程序会得到该错误:
Exception in thread “main” java.lang.NullPointerException
atjavax.xml.bind.DatatypeConverter.printHexBinary(DatatypeConverter.java:560)
atReturnDigestUserInterface.main(ReturnDigestUserInterface.java:21)
问题在于main方法可能在工作线程执行完毕之前结束,所以得到的digist是空。
轮询
大多数新手的解决方案,然后主程序定期询问获取方法
public static void main(String[] args){
ReturnDigest[] digeists = new ReturnDigest[args.length];
for(int i=0;i<args.length;i++){
digeists[i] = new ReturnDigest(args[i]);
digeists[i].start();
}
for(int i=0;i<digeists.length;i++){
while(true){
//显示结果
byte[] digeist = digeists[i].getDigest();
if(digeist != null){
StringBuilder result = new StringBuilder(args[i]);
result.append(":");
result.append(DatatypeConverter.printHexBinary(digeist));
System.out.println(result);
break;
}
}
}
}
但是在有些虚拟机上,主线程会占用所有可用的时间,而没有口机会给其他线程以至于没时间来完成具体任务。
回调(完全可以淘汰前面的做法)
与轮询不同,该方法是让线程来告诉主程序如何结束,线程完成时,反过来回调其创建者。
public class CallbackDigest implements Runnable{
private String filename;
public CallbackDigest(String filename) {
this.filename = filename;
}
@Override
public void run() {
try {
FileInputStream in = new FileInputStream(filename);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
/*调用主函数的回调方法*/ CallbackDigestUserInterface.receiveDigest(digest, filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//主方法
public class CallbackDigestUserInterface {
//供线程调用的静态方法,不需要实例引用
public static void receiveDigest(byte[] digest,String name){
StringBuilder result = new StringBuilder(name);
result.append(":");
result.append(DatatypeConverter.printHexBinary(digest));
System.out.println(result);
}
public static void main(String[] args){
for(String filename: args){
CallbackDigest cb = new CallbackDigest(filename);
Thread t = new Thread(cb);
t.start();
}
}
}
使用静态方法回调线程类只需要知道回调的方法名即可,但更常见是是回调实例方法,更常见的也是回调实例方法。
这种情况下,进行回调的类就必须有一个回调对象的引用。通常都在线程的构造函数中以参数的方式提供。
//线程
public class InstanceCakkbackDigest implements Runnable{
private String filename;
private InstanceCallbackDigestUserInterface callback;
public InstanceCakkbackDigest(String filename,
InstanceCallbackDigestUserInterface callback) {
this.filename = filename;
//将回调对象传入线程,在工作执行完毕时调用其中的方法
this.callback = callback;
}
@Override
public void run() {
try {
FileInputStream in = new FileInputStream(filename);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//主程序
public class InstanceCallbackDigestUserInterface {
private String filename;
private byte[] digest;
public InstanceCallbackDigestUserInterface(String filename){
this.filename = filename;
}
//在方法中创建工作线程,并将本身的引用传入工作线程以便回调
public void caculateDigest(){
InstanceCakkbackDigest cb = new InstanceCakkbackDigest(filename, this);
Thread t = new Thread(cb);
t.start();
}
void receiveDigest(byte[] digest){
this.digest = digest;
System.out.println(this);
}
@Override
public String toString() {
String result = filename + ":";
if(digest != null){
result += DatatypeConverter.printHexBinary(digest);
}else{
result += "digest not available";
}
return result;
}
public static void main(String[] args){
for(String filename : args){
InstanceCallbackDigestUserInterface d
= new InstanceCallbackDigestUserInterface(filename);
d.caculateDigest();
}
}
}