读书人

回调思维(稍为涉及线程、摘要信息)

发布时间: 2012-12-25 16:18:28 作者: rapoo

回调思想(稍为涉及线程、摘要信息)

自己尝试把今天上午的学习整理一下,代码大部分是《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){getif(!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.threadReturnDigestThread返回进程中的信息ReturnDigestUserInterface 处理进程返回的信息CallbackDigest与下者配合,实现回调,主方法中构造该类实例,并启动,run中实现静态回调CallbackDigestUserInterface静态回调方法InstanceCallbackDigest实现Runnable接口,run中实现计算并回调给用户(下者)InstanceCallbackDigestUserInterface 实例用户接口对象,调用计算方法,计算方法里里面构造上者线程并启动(前面的静态回调是构造的实例不同哦)DigestListener声明了回调方法的接口ListCallbackDigest含有一个列表成员,存储需要需要回调的监听用户(该用户实现了DigestListener接口),回调的方式不在直接跟监听用户通信,而是用一个sendDigest的方法,遍历列表成员,调用它们实现的接口方法ListCallbackDigestUserInterface实现了DigestListener的接口整个过程就是,实例ListCallbackDigest,增加监听对象,启动线程,run中取得摘要信息后,调用sendDigest()方法,里面遍历监听列表中的每个监听用户对象调用各自实现DigestListener接口的方法*/

?

读书人网 >编程

热点推荐