前言
最近因为一些个人原因,未能抽出太多精力更新Java多线程系列,一搁置就是好几个月,先向读者诸君致歉。
在本系列的其他文章中,已经提到过线程之间的相互协作, 通过分工,将程序系统的不同任务进行线程分离,充分利用机器性能、提升特定线程的利用率和程序的体验感。
详见拙作:Java多线程基础–线程生命周期与线程协作详解.
并在线程池相关文章中提到:作为程序构建者,我们更关心线程(组)的特性和它们所执行的任务,并不愿意分心去做线程操作。
然而实际开发中,我们同样关心一个任务对程序系统产生的影响,习惯上称之为任务的的执行结果。
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());