c3 Threads - Returning Infomation From a Thread

来源:互联网 发布:sql virtual table 编辑:程序博客网 时间:2024/06/04 23:23

Returning Info from a Thread

方案一

class DigestThread extends Thread{private String filename;private byte[] digest;public DigestThread(String fileName){this.filename = fileName;}@Overridepublic 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 (IOException ex) {      System.err.println(ex);    } catch (NoSuchAlgorithmException ex) {      System.err.println(ex);    }}public byte[] getDigest(){return digest;}}

  public static void main(String[] args) {    for (String filename : args) {      // Calculate the digest      DigestThread dr = new DigestThread(filename);      dr.start();      // Now print the result      StringBuilder result = new StringBuilder(filename);      result.append(": ");      byte[] digest = dr.getDigest();      result.append(DatatypeConverter.printHexBinary(digest));      System.out.println(result);    }  }

输出会报异常“NullPointerException”,因为getDigest()时,digest还未被初始化,也就是那个线程还没有执行完毕。


方案二:

public static void main(String[] args) throws Exception {DigestThread[] digests = new DigestThread[args.length];for(int i=0;i<args.length;i++){digests[i] = new DigestThread(args[i]);digests[i].start();}for(int i=0;i<args.length;i++){StringBuilder result = new StringBuilder(args[i]);result.append(": ");byte[] digest = digests[i].getDigest();result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);}}

如果幸运,会得到想要的结果,但很可能还会得到空指针异常。因为主线程去getDigest时,子线程还未完成。

你能获得的是正常的结果,还是异常还是程序被挂起,依赖于很多因素,包括程序衍生了多少个线程,当前系统的cup和disk的运行速度,系统用了多少cpu,jvm对不同线程分配时间的算法。这叫做“race condition”。


方案三:

public static void main(String[] args) throws Exception {DigestThread[] digests = new DigestThread[args.length];for(int i=0;i<args.length;i++){digests[i] = new DigestThread(args[i]);digests[i].start();}for(int i=0;i<args.length;i++){while(true){byte[] digest = digests[i].getDigest();if(digest != null){StringBuilder result = new StringBuilder(args[i]);result.append(": ");result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);break;}}}}

菜鸟一般都这么搞,这样做有时也能完成工作,但输出顺序完全按线程启动的顺序,而“忽略、屏蔽”了不同线程间速度的差异,显然,效率上差,做了无用功。但在一些虚拟机上,主线程占用了所有可用时间,而没有时间留给子线程去工作,那在这样的情况下,就不行了。


下面的方案是我应用观察者模式写的:

class DigestTask extends Observable implements Runnable{private String filename;private byte[] digest;public DigestTask(String fileName){this.filename = fileName;}@Overridepublic void run() {try {System.out.println("DigestTask "+filename+"-"+Thread.currentThread().getName());      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();            this.setChanged();      this.notifyObservers();    } catch (IOException ex) {      System.err.println(ex);    } catch (NoSuchAlgorithmException ex) {      System.err.println(ex);    }}public byte[] getDigest(){return digest;}public String getFileName(){return filename;}}

public static void main(String[] args) throws Exception {DigestTask[] digests = new DigestTask[args.length];for(int i=0;i<args.length;i++){digests[i] = new DigestTask(args[i]);digests[i].addObserver(new Observer() {@Overridepublic void update(Observable o, Object arg) {outputDigest(((DigestTask)o).getFileName(), ((DigestTask)o).getDigest());}});Thread t = new Thread(digests[i]);t.start();}}

static void outputDigest(String filename,byte[] digest){StringBuilder result = new StringBuilder(filename);result.append(": ");result.append(DatatypeConverter.printHexBinary(digest));System.out.println("outputDigest "+filename+"-"+Thread.currentThread().getName());System.out.println(result);}

这样只是客户端可以自定义当子线程执行完成后的行为, 本质上还是没有从执行的线程返回数据到主线程。


但接下来作者的解决方案是“CallBack”。“Observer Patern”,一般的callback可以设置一个回调对象,观察者呢可以设置一组,所有对该内容变化感兴趣的callback。


class InstanceCallbackDigest implements Runnable {private String filename;private InstanceCallbackDigestUserInterface callback;public InstanceCallbackDigest(String filename,InstanceCallbackDigestUserInterface callback) {this.filename = filename;this.callback = callback;}@Overridepublic void run() {try {FileInputStream in = new FileInputStream(filename);MessageDigest sha = MessageDigest.getInstance("SHA-256");DigestInputStream din = new DigestInputStream(in, sha);while (din.read() != -1); // read entire filedin.close();byte[] digest = sha.digest();callback.receiveDigest(digest);} catch (Exception ex) {System.err.println(ex);}}}class InstanceCallbackDigestUserInterface {private String filename;private byte[] digest;public InstanceCallbackDigestUserInterface(String filename) {this.filename = filename;}public void calculateDigest() {InstanceCallbackDigest cb = new InstanceCallbackDigest(filename, this);Thread t = new Thread(cb);t.start();}void receiveDigest(byte[] digest) {this.digest = digest;System.out.println(this);}@Overridepublic 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) {// Calculate the digestInstanceCallbackDigestUserInterface d = new InstanceCallbackDigestUserInterface(filename);d.calculateDigest();}}}

一个单独的类可以当作一个数据结构!


Futures,Callables,Executors

jdk5 提供了方便的使用多线程的方式ExecutorService,接受Callable的实现类,针对每一个Callable都会返回一个Future,通过Future可以获得线程执行的结果,如果线程还未执行完毕,那调用线程会block,直到有结果,如果有结果,那就会立即返回。


假设现在有个任务,在一个数组中找出最大的数,可以用2个线程在做,基本上速度会是单线程的2倍。

class MultithreadedMaxFinder {public static int max(int[] data) throws InterruptedException,ExecutionException {if (data.length == 1) {return data[0];} else if (data.length == 0) {throw new IllegalArgumentException();}// split the job into 2 piecesFindMaxTask task1 = new FindMaxTask(data, 0, data.length / 2);FindMaxTask task2 = new FindMaxTask(data, data.length / 2, data.length);// spawn 2 threadsExecutorService service = Executors.newFixedThreadPool(2);Future<Integer> future1 = service.submit(task1);Future<Integer> future2 = service.submit(task2);return Math.max(future1.get(), future2.get());}}class FindMaxTask implements Callable<Integer> {private int[] data;private int start;private int end;FindMaxTask(int[] data, int start, int end) {this.data = data;this.start = start;this.end = end;}@Overridepublic Integer call() throws Exception {int max = Integer.MIN_VALUE;for (int i = start; i < end; i++) {if (data[i] > max)max = data[i];}return max;}}


注意:调用future1.get()时,如果有结果则立即返回,如果还无结果,则block,等着,直到有结果了,future2.get()也是如此。



0 0
原创粉丝点击