java多线程学习二

来源:互联网 发布:数控旋压机编程 编辑:程序博客网 时间:2024/06/05 11:06

运行线程

目前在java的线程学习中,我学到的创建、运行线程的方法主要有三种。
方法一、第一种是直接构造Thread类的一个实例,调用它的start()方法。要让线程完成一些操作,可以对Thread类派生子类,覆盖其run()方法。
下面的例子源于书本,该程序用于计算多个文件的安全散列算法(SHA)的摘要。 DigestThread是Thread的子类,它的run()方法为指定文件计算一个256位的
SHA-2消息摘要。为此要用一个DigestInputStream来读取这个文件。读取结束时,可以从digest()方法种得到这个散列。
例子一:
package com.HL.DigestThread;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.security.DigestInputStream;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import javax.swing.filechooser.FileNameExtensionFilter;import javax.xml.bind.DatatypeConverter;import javax.xml.bind.DatatypeConverterInterface;public class DigestThread extends Thread{private String fileName;public DigestThread(String fileName){this.fileName = fileName;}@Overridepublic void run() {// TODO Auto-generated method stubtry {FileInputStream inputStream = new FileInputStream(fileName);try {MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");DigestInputStream digestInputStream = new DigestInputStream(inputStream, messageDigest);try {while(digestInputStream.read() != -1);digestInputStream.close();byte[]digest = messageDigest.digest();StringBuilder result = new StringBuilder(fileName);result.append(":");result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}super.run();}public static void main(String[] args) {for(String filename :args){Thread t = new DigestThread(filename);t.start();}}}
main()方法从命令行中读取文件名,针对每个文件名启动一个新的DigestThread。
注意,这里Thread的派生子类,只应当覆盖run()方法,而不应该覆盖其它方法,例如start(),join()等标准方法。


方法二、为了避免覆盖Thread的标准方法,推荐编写一个Runnable的实例,将线程需要 完成的任务包装在run()中。
第二个例子将第一个例子改写使用了runnable接口,把extends Thread改为了implements Runnable,并在main()方法里把DigestRunnable对象传给了Thread的
构造函数,程序的基本逻辑没有改变。

例子二:
package com.HL.DigestThread;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.security.DigestInputStream;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import javax.swing.filechooser.FileNameExtensionFilter;import javax.xml.bind.DatatypeConverter;import javax.xml.bind.DatatypeConverterInterface;public class DigestThreadRunnable implements Runnable{private String fileName;public DigestThreadRunnable(String fileName){this.fileName = fileName;}@Overridepublic void run() {// TODO Auto-generated method stubtry {FileInputStream inputStream = new FileInputStream(fileName);try {MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");DigestInputStream digestInputStream = new DigestInputStream(inputStream, messageDigest);try {while(digestInputStream.read() != -1);digestInputStream.close();byte[]digest = messageDigest.digest();StringBuilder result = new StringBuilder(fileName);result.append(":");result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}super.run();}public static void main(String[] args) {for(String filename :args){DigestThreadRunnable dr = new DigestThreadRunnable(filename);Thread thread  = new Thread(dr);thread.start();}}}


方法三:Executor和callable创建线程。这里涉及到一个知识点,如何从线程返回信息,这是多线程编程中最常被误解的方面之一。run()和start()方法本身不返回任何值,,大多数人的第一个反应是把结果存储在一个字段中,再提供一个获取方法,如下面的例子所示。
public byte[] getDigest() {return digest;}

在主程序中使用存取方法取得线程输出
package com.HL.DigestThread;import javax.xml.bind.DatatypeConverter;public class ReturnDigestTest {public static void main(String[] args) {for(String fileName : args){DigestThread dt = new DigestThread(fileName);dt.start();//显示结果StringBuilder result = new StringBuilder(fileName);result.append(":");byte[]digest  = dt.getDigest();result.append(DatatypeConverter.printHexBinary(digest));System.out.println(result);}}}

但这样做是存在问题的,我们可能得不到正确的输出。因为dt.start()启动的计算可能在dt.getDigest()之前还没有结束,也可能结束了,主线程是不会等子线程的。如果还没有结束,dt.getDigest()则会返回null,此时访问digest会抛出异常。

Java5 引入了多线程编程的一个新方法,通过隐藏细节可以更容易的处理回调。我们不再需要直接创建一个线程,是需要创建一个ExecutorService,它会根据你的需要创建线程,可以向ExecutorService提交Callable任务,对于每个Callable任务,会分别得到一个Future,之后可以向Future请求得到任务的结果。

下面这个例子,用于找出一个数字数组中的最大值,将一个任务分配到了两个线程中运行,这样比单线程运行快不少。 

例子二:
package com.HL.callableTest;import java.util.concurrent.Callable;public class FindMaxTask implements Callable<Integer>{private int[]data;private int start;private int end;public FindMaxTask(int[]data,int start,int end ) {// TODO Auto-generated constructor stubthis.data = data;this.start = start;this.end = end;}@Overridepublic Integer call() throws Exception {// TODO Auto-generated method stubint max = Integer.MIN_VALUE;for(int i = start; i < end; i++){if(data[i] > max) max = data[i];}return max;}}


package com.HL.callableTest;import java.util.concurrent.ExecutionException;import java.util.concurrent.Executor;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class ThreadMaxFinder {public static int max(int[]data) throws InterruptedException, ExecutionException {if(data.length == 1)return data[0];FindMaxTask task1 = new FindMaxTask(data, 0, data.length/2);FindMaxTask task2 = new FindMaxTask(data, data.length/2, data.length);//创建两个线程ExecutorService service = Executors.newFixedThreadPool(2);Future<Integer> future1 = service.submit(task1);Future<Integer> future2 = service.submit(task2);return Math.max(future1.get(), future2.get());}public static void main(String[] args) {try {System.out.println(String.valueOf(max(new int[]{12,5,16})));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

在最后一句Math.max(future1.get(), future2.get())中,调用future1.get()时,这个方法会阻塞,只有当第一个FindMaxTask结束时,才会调用future2.get()。一旦两个线程都结束,将比较它们的结果,并返回最大值。


0 0
原创粉丝点击