自己尝试把今天上午的学习整理一下,代码大部分是《java网络编程》中的源码,自己做了一点点修改,本文涉及主要类和接口有 Thread、Runnable、DigestInputStream、FileInputStream、MessageDigest等
程序主要有两个部分:线程中获得文件摘要信息 和 用户打印这些信息
使用简单的存取方法获得线程输出
使用存取方法返回线程结果:
ReturnDigest :
public class ReturnDigest extends Thread {
private File input;
private byte[] digest;
public ReturnDigest(File input) {
this.input = input;
}
public void run() {
try {
FileInputStream in = new FileInputStream(input);
MessageDigest sha = MessageDigest.getInstance("SHA");
DigestInputStream din = new DigestInputStream(in, sha);
int b;
//需要全部读完才能获得摘要消息
while ((b = din.read()) != -1);
din.close();
digest = sha.digest();
// System.out.println(digest);
} catch (IOException ex) {
System.err.println(ex);
} catch (NoSuchAlgorithmException ex) {
System.err.println(ex);
}
}
public byte[] getDigest(){
return digest;
}
}
使用存取方法获得线程输出:
ReturnDigestUserInterface :
public class ReturnDigestUserInterface {
public static void main(String[] args) throws InterruptedException {
String[] strArray = new String[] { "a.txt", "b.txt", "c.txt" };
for (String str : strArray) {
//计算摘要
File f = new File(str);
ReturnDigest dr = new ReturnDigest(f);
dr.start();
//显示信息
StringBuffer result = new StringBuffer(f.toString());
result.append(": ");
// while(dr.getDigest() == null);
byte[] digest = dr.getDigest();
for(int i = 0; i < digest.length; i++){
result.append(digest[i] + " ");
}
System.out.println(result);
}
}
}
以上两段代码作为背景,只为说明了程序的目的,含有很多缺陷,例如 主程序中, dr.getDigest(); 返回的可能是一个null对象,因为线程中获得digest的代码还没跑完。
轮询
新手使用较多(我之前也是这么干的,呵呵)
//可以像代码中注释,使用
while(dr.getDigest() == null);
//或者
while(true){
get
if(!null)
print
}
回调
轮询浪费太多的cpu时间了!
这里使用回调思想,总体思想是:主程序中不再直接获得线程信息然后处理,而是线程获得信息后,回调给用户接口类。
(第一次接触这种思想是在学习awt的时候,第一次应用是在写一个贪吃蛇游戏的时候)
以下循环渐进,介绍几种利用了回调思想的方法
静态回调:
CallbackDigest :
public class CallbackDigest implements Runnable {
private File input;
public CallbackDigest(File input) {
this.input = input;
}
public void run() {
try {
FileInputStream in = new FileInputStream(input);
MessageDigest sha = MessageDigest.getInstance("SHA");
DigestInputStream din = new DigestInputStream(in, sha);
int b;
while ((b = din.read()) != -1)
;
din.close();
byte[] digest = sha.digest();
// 这里没有返回信息,而是完成后,回调(触发)用户类方法
CallbackDigestUserInterface.receiveDigest(digest, input.getName());
} catch (IOException ex) {
System.err.println(ex);
} catch (NoSuchAlgorithmException ex) {
System.err.println(ex);
}
}
}
CallbackDigestUserInterface :
import java.io.File;
public class CallbackDigestUserInterface {
//这里receiveDIgest()没有放在主线程,而是由每个线程单独调用
public static void receiveDigest(byte[] digest, String name) {
StringBuffer result = new StringBuffer(name);
result.append(": ");
for (int i = 0; i < digest.length; i++) {
result.append(digest[i] + " ");
}
System.out.println(result);
}
public static void main(String[] args) {
String[] strArray = new String[]{"a.txt","b.txt","c.txt"};
for(String str : strArray){
File f = new File(str);
CallbackDigest cb = new CallbackDigest(f);
Thread t = new Thread(cb);
t.start();
}
}
}
主程序中 构造CallbackDigest 实例并创建、启动线程,run()方法中,获得摘要信息digest后,直接调用用户类CallbackDigestUserInterface 的 receiveDigest() 方法,这样轻松实现了程序功能,同时避免了 “轮询” ,节省cpu时间。
实例方法回调:
InstanceCallbackDigest :
public class InstanceCallbackDigest implements Runnable {
private File input;
private InstanceCallbackDigestUserInterface callback;
public InstanceCallbackDigest(File input,
InstanceCallbackDigestUserInterface callback) {
this.input = input;
this.callback = callback;
}
public void run() {
try {
FileInputStream in = new FileInputStream(input);
MessageDigest sha = MessageDigest.getInstance("SHA");
DigestInputStream din = new DigestInputStream(in, sha);
int b;
while ((b = din.read()) != -1)
;
din.close();
byte[] digest = sha.digest();
callback.receiveDigest(digest);
} catch (IOException ex) {
System.err.println(ex);
} catch (NoSuchAlgorithmException ex) {
System.err.println(ex);
}
}
}
InstanceCallbackDigestUserInterface :
public class InstanceCallbackDigestUserInterface {
private File input;
private byte[] digest;
public InstanceCallbackDigestUserInterface(File input) {
this.input = input;
}
public void calculateDigest() {
InstanceCallbackDigest cb = new InstanceCallbackDigest(input, this);
Thread t = new Thread(cb);
t.start();
}
void receiveDigest(byte[] digest){
this.digest = digest;
System.out.println(this);
}
public String toString(){
String result = input.getName() + ": ";
if(digest != null){
for(int i = 0; i < digest.length; i++){
result += digest[i] + " ";
}
}
else{
result = " digest not available";
}
return result;
}
public static void main(String[] args) {
String[] strArray = new String[]{"a.txt","b.txt","c.txt"};
for(String str : strArray){
File f = new File(str);
InstanceCallbackDigestUserInterface d = new InstanceCallbackDigestUserInterface(f);
d.calculateDigest();
}
}
}
相对之前的静态回调,摘要类InstanceCallbackDigest 多了一个InstanceCallbackDigestUserInterface 类成员 callback 。主程序 构造InstanceCallbackDigestUserInterface 实例(文件参数),调用calculateDigest()方法,该方法中利用构造了摘要类InstanceCallbackDigest 实例(用户类参数 user,文件参数),并创建、启动线程,run()方法中获得摘要后,调用user的 receiveDigest()方法,该方法打印了信息。
实例方法相对复杂一些,优点:每个主类InstanceCallbackDigest 映射一个File文件,用户接口类不需要额外的数据结构去获得文件的信息。而且,这个实例必要时可以重新计算某个文件的摘要,相当灵活,这种设计方法经常被使用。
回调对象列表:
如果关心摘要信息的用户接口类不只是一个呢?可以可以利用一个列表对象保存 所有 关心线程中获得的摘要信息 的用户接口实例,所有的这些用户接口实例 都要实现同一个接口,该接口声明了如何处理摘要信息的方法。
接口DigestListener :
public interface DigestListener {
//回调:处理线程中获得的digest信息
public void digestHandle(byte[] digest);
}
ListCallbackDigest :
public class ListCallbackDigest implements Runnable {
private File input;
List listenerList = new Vector();
public ListCallbackDigest(File input){
this.input = input;
}
public synchronized void addDigestListener(DigestListener l) {
listenerList.add(l);
}
public synchronized void removeDigestListener(DigestListener l) {
listenerList.remove(l);
}
private synchronized void sendDigest(byte[] digest){
Iterator iterator = listenerList.listIterator();
while(iterator.hasNext()){
DigestListener dl = (DigestListener) iterator.next();
dl.digestHandle(digest);
}
}
public void run() {
try {
FileInputStream in = new FileInputStream(input);
MessageDigest sha = MessageDigest.getInstance("SHA");
DigestInputStream din = new DigestInputStream(in, sha);
int b;
while ((b = din.read()) != -1)
;
din.close();
byte[] digest = sha.digest();
//这里的回调不再把摘要信息直接发送给注册的“监听者”他不再关心用户接口类的事情。
this.sendDigest(digest);
}catch (IOException ex) {
System.err.println(ex);
}
catch(NoSuchAlgorithmException ex){
System.err.println(ex);
}
}
}
ListCallbackDigestUserInterface :
public class ListCallbackDigestUserInterface implements DigestListener {
private File input;
private byte[] digest;
public ListCallbackDigestUserInterface(File input) {
this.input = input;
}
public void calculateDigest() {
ListCallbackDigest cb = new ListCallbackDigest(input);
cb.addDigestListener(this);
Thread t = new Thread(cb);
t.start();
}
public void digestHandle(byte[] digest) {
this.digest = digest;
System.out.println(this);
}
public String toString() {
String result = input.getName() + ": ";
if (digest != null) {
for (int i = 0; i < digest.length; i++) {
result += digest[i] + " ";
}
}
else {
result = " digest not available";
}
return result;
}
public static void main(String[] args) {
String[] strArray = new String[]{"a.txt","b.txt","c.txt"};
for(String str : strArray){
File f = new File(str);
ListCallbackDigestUserInterface d = new ListCallbackDigestUserInterface(f);
d.calculateDigest();
}
}
}
主程序中,构造了用户接口实例,用户调用calculateDigest()方法,该方法构造摘要类ListCallbackDigest 实例,向其中注册监听对象(这里只add了一个,可以add多个),然后创建并启动线程,run()中获得摘要digest后,也不是立刻跟监听的用户通信,而是调用一个sendDigest()方法,该方法遍历摘要类的成员(注册的监听用户对象列表),在循环中调用各自的digestHandle()方法(实现接口的方法)。
/*
com.yong.thread
ReturnDigestThread 返回进程中的信息
ReturnDigestUserInterface 处理进程返回的信息
CallbackDigest 与下者配合,实现回调,主方法中构造该类实例,并启动,run中实现静态回调
CallbackDigestUserInterface 静态回调方法
InstanceCallbackDigest 实现Runnable接口,run中实现计算并回调给用户(下者)
InstanceCallbackDigestUserInterface 实例用户接口对象,调用计算方法,计算方法里里面构造上者线程并启动(前面的静态回调是构造的实例不同哦)
DigestListener 声明了回调方法的接口
ListCallbackDigest 含有一个列表成员,存储需要需要回调的监听用户(该用户实现了DigestListener接口),
回调的方式不在直接跟监听用户通信,而是用一个sendDigest的方法,遍历列表成员,调用它们实现的接口方法
ListCallbackDigestUserInterface 实现了DigestListener的接口
整个过程就是,实例ListCallbackDigest,增加监听对象,启动线程,run中取得摘要信息后,调用sendDigest()方法,里面遍历监听列表中的每个监听用户对象
调用各自实现DigestListener接口的方法
*/