Runnable的局限性
在前文中我们谈到,通过编码实现Runnable接口,将获得具有边界性的 "任务",在指定的线程(或者线程池)中运行。
重新观察该接口,不难发现它并没有方法返回值:
public interface Runnable { void run(); } 复制代码
在JDK1.5之前,想利用任务的执行结果,需要小心的操作线程访问临界区资源。使用 回调
进行解耦是非常不错的选择。
练手小Demo -- 回顾既往文章知识
注意,为了减少篇幅使用了lambda,但jdk1.5之前并不支持lambda
将计算任务分离到其他线程执行,再回到主线程消费结果
我们将计算、IO等耗时任务丢到其他线程,让主线程专注于自身业务, 假想它在接受用户输入以及处理反馈,但我们略去这一部分
我们可以设计出类似下面的代码:
虽然它还有很多不合理之处值得优化,但也足以用于演示
class Demo { static final Object queueLock = new Object(); static List<Runnable> mainQueue = new ArrayList<>(); static boolean running = true; static final Runnable FINISH = () -> running = false; public static void main(String[] args) { synchronized (queueLock) { mainQueue.add(Demo::onStart); } while (running) { Runnable runnable = null; synchronized (queueLock) { if (!mainQueue.isEmpty()) runnable = mainQueue.remove(0); } if (runnable != null) { runnable.run(); } Thread.yield(); } } public static void onStart() { //... } public static void finish() { synchronized (queueLock) { mainQueue.clear(); mainQueue.add(FINISH); } } } 复制代码
再模拟一个计算的线程和任务回调:
interface Callback { void onResultCalculated(int result); } class CalcThread extends Thread { private final Callback callback; private final int a; private final int b; public CalcThread(Callback callback, int a, int b) { this.callback = callback; this.a = a; this.b = b; } @Override public void run() { super.run(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } final int result = a + b; System.out.println("threadId" + Thread.currentThread().getId() + ",calc result:" + result + ";" + System.currentTimeMillis()); synchronized (queueLock) { mainQueue.add(() -> callback.onResultCalculated(result)); } } } 复制代码
填充一下onStart业务:
class Demo { public static void onStart() { System.out.println("threadId" + Thread.currentThread().getId() + ",onStart," + System.currentTimeMillis()); new CalcThread(result -> { System.out.println("threadId" + Thread.currentThread().getId() + ",onResultCalculated:" + result + ";" + System.currentTimeMillis()); finish(); }, 200, 300).start(); } } 复制代码
复习:优化为使用Runnable
在前文我们提到,如果业务仅关注任务的执行,并不过于关心线程本身,则可以利用Runnable:
class Demo { static class CalcRunnable implements Runnable { private final Callback callback; private final int a; private final int b; public CalcRunnable(Callback callback, int a, int b) {