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呢?

posted @ 2020-07-07 23:10  HeliusKing  阅读(727)  评论(0编辑  收藏  举报