Runnable回调
一、如何从线程中返回信息
1、轮询
在新起线程中,设置一个标志字段,并提供一个get
方法,main
方法中轮询,判断这个标志字段的值。
该方案固然可行,但做了大量不需要的工作。
2、回调
直接看代码。⤵️
主线程:
import javax.xml.bind.DatatypeConverter;
/**
* @Author Helius
* @Create 2020-07-07-10:01 下午
*/
public class CallbackDigestInterface {
//主线程中被工作线程回调的方法,目的是输出。
//注意用的是静态方法回调,
public static void receiveDigest(byte[] digest, String name) {
StringBuilder result = new StringBuilder(name);
result.append(": ");
result.append(DatatypeConverter.printHexBinary(digest));
System.out.println(result);
}
public static void main(String[] args) {
for (String filename : args) {
CallbackDigest cb = new CallbackDigest(filename);
Thread thread = new Thread(cb);
thread.start();
}
}
}
工作线程:
public class CallbackDigest implements Runnable {
private String filename;
public CallbackDigest(String filename) {
this.filename = filename;
}
//从文件流中读取文件,并获取文件的摘要
@Override
public 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();
byte[] digest = sha.digest();
CallbackDigestInterface.receiveDigest(digest, filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
实际编码中,用的更多的是回调实例方法,工作线程类持有一个引用,并通过其构造函数进行赋值。
如果是多个对象对结果感兴趣,
1、可以在回调类中增加一个注册方法,其他对象在本类中调用并注册,回调类持有一个类似List集合的引用
2、或者回调类持有某个接口的引用,其他对结果感兴趣的类实现该接口, 回调类中通过反射来通知这些感兴趣的实现类,
咦, 这不就是观察者模式么,哈哈。
3、Callable回调
显然,我们能想到的,jdk也能想的到,jdk1.5之后的并发包,提供了Callable
接口,可以获取线程的返回值。
只不过就是隐藏了回调的实现细节。
🚲举例:找出一个很大的数字数组的最大值
如果采用原始方法,需要的时间为O(n),
其中n为数组的元素个数
如果我们将这个工作分解到多个工作线程上去,每个线程分别在一个单独的内核上进行,就会快的多
直接上代码:
FindMaxTask
类
public class FindMaxTask implements Callable<Integer> {
private int[] data;
private int start;
private int end;
public FindMaxTask(int[] data, int start, int end) {
this.data = data;
this.start = start;
this.end = end;
}
//找出数组中索引从start 到end这个部分的最大值
@Override
public Integer call() throws Exception {
int max = Integer.MAX_VALUE;
for (int i = start; i < end; i++) {
if (data[i] > max) max = data[i];
}
return max;
}
}
MultithreadedMaxFinder
类
public class MultithreadedMaxFinder {
public static int max(int[] data) throws ExecutionException, InterruptedException {
//简单校验
if (data.length == 1) {
return data[0];
} else if (data.length == 0) {
throw new IllegalArgumentException();
}
FindMaxTask task1 = new FindMaxTask(data, 0, data.length / 2);
FindMaxTask task2 = new FindMaxTask(data, data.length / 2, data.length);
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future1 = pool.submit(task1);
Future<Integer> future2 = pool.submit(task2);
return Math.max(future1.get(),future2.get());
}
}
每个线程都会找寻一个分段中的最大值,最从结果中找最大值,就是我们需要的结果
需要注意的是futere.get()会阻塞,等待线程任务完成
哈哈,是否又想到了ForkJoinPool
呢?
你所看得到的天才不过是在你看不到的时候还在努力罢了!