当我们需要计算多个文件的消息摘要时,程序在单线程里执行是没问题的,不过相对用多线程处理起来,速度比较快,但多个线程处理完之后,我们怎么获取得线程执行的相关信息呢
我们知道通过执行Thread的start()就可以启动线程,而线程的任务是在run()中执行的。但线程执行结束后,我们该如何独得线程执行的信息呢?或许我们可以在线程类定义个方法,提供给线程的启动类调用,但是线程什么时候才执行完呢;另者,线程的run()执行完后主动将消息提供给启动类,这种方法比较有保障。
所以获取线程执行的信息有两个方法:
1、轮询
2、回调
一、轮询
在启动类里启动了线程后,启动类无限循环地去询问线程是否已经执行完。
private static String[] files = {
"f:\\java.txt",
"f:\\c.txt",
"f:\\c++.txt",
"f:\\javascript.txt",
"f:\\html.txt"
};
private static DigestThread[] threads;
public static void main(String[] args) throws Exception{
threads = new DigestThread[files.length];
for(int i = 0 ; i < files.length ; i++){
threads[i] = new DigestThread("F:\\" + files[i]);
threads[i].start();
}
for(int j = 0 ; j < threads.length ; j++){
byte[] message = null;
while(true){
message = threads[j].getDigest();
if(message != null){
StringBuilder sb = new StringBuilder(threads[j].getFile().getAbsolutePath());
sb.append(":");
for(byte meg : message){
sb.append(meg);
}
System.out.println("polling " + sb.toString());
break;
}
}
}
}
DigestThread类:
public class DigestThread extends Thread{
private File input;
private byte[] message;
public DigestThread(String fileName){
this(new File(fileName));
}
public DigestThread(File input){
this.input = input;
}
@Override
public void run() {
digest();
}
public void digest(){
DigestInputStream din = null;
try {
FileInputStream in = new FileInputStream(input);
MessageDigest sha = MessageDigest.getInstance("SHA");
din = new DigestInputStream(in, sha);
int b;
while((b = din.read()) != -1);
message = sha.digest();
} catch (FileNotFoundException e) {
System.err.println(e);
} catch (NoSuchAlgorithmException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(din != null){
din.close();
din = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public byte[] getDigest(){
return this.message;
}
public File getFile(){
return this.input;
}
}
但在很多情况下,多次来询问线程时线程还没执行完,询问无功而返,这样就做了较多无用的操作。
二、回调
回调比轮询比较简单有效,不用再无限循环地询问线程是否执行完了。线程执行完后主动将消息告知启动类,那么启动类在线程执行过程中就可以休息,主要线程来通知它消息就可以了。
Callbackable:
public interface Callbackable {
public void callback(byte[] bytes);
}
Calculatable:
public interface Calculatable {
public void calculate();
}
CallbackListDigest:
线程类,将线程执行完后需要通知的启动类储放在列表callbacks中,这样设计可以通知不只一个类,所以需要知道线程执行的信息的类都添加到列表即可。
public class CallbackListDigest implements Runnable{
private File file;
private List<Callbackable> callbacks = new ArrayList<Callbackable>();
public CallbackListDigest(File file) {
this.file = file;
}
public void addCallback(Callbackable callback){
callbacks.add(callback);
}
public void removeCallback(Callbackable callback){
callbacks.remove(callback);
}
@Override
public void run() {
byte[] bytes = digest();
sendDigest(bytes);
}
private byte[] digest(){
DigestInputStream d = null;
try {
InputStream in = new FileInputStream(file);
MessageDigest md = MessageDigest.getInstance("SHA");
d = new DigestInputStream(in, md);
int b;
while( (b = d.read()) != -1);
return md.digest();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if( d != null){
d.close();
d = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private void sendDigest(byte[] bytes){
Iterator<Callbackable> iterator = callbacks.iterator();
while(iterator.hasNext()){
iterator.next().callback(bytes);
}
}
}
CalculateInstance:
public class CalculateInstance implements Calculatable, Callbackable{
private File file;
public CalculateInstance(File file) {
this.file = file;
}
@Override
public void calculate() {
CallbackListDigest d = new CallbackListDigest(file);
d.addCallback(this);
Thread thread = new Thread(d);
thread.start();
}
@Override
public void callback(byte[] bytes) {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName()).append(" ");
sb.append(file.toString()).append(":");
for(byte b : bytes){
sb.append(b);
}
System.out.println(sb.toString());
}
}
测试程序:
for(int i = 0 ; i < files.length ; i++){
(new CalculateInstance(new File("F:\\" + files[i]))).calculate();
}