本文内容总结于《Java高并发编程详解 多线程与架构设计》
观察者模式回顾
关于具体实现,本文不再做相关介绍,可以查看链接->观察者模式案例。
观察者模式的核心可以总结为:
- 主题持有观察者的引用,可以在主题状态变化时通知观察者;
- 主题内部维护了一个集合保存所有观察者,用以特定通知或广播;
总结的再精炼一点,观察者模式其实就是回调机制的体现和延伸而已。
实现案例:监控任务的生命周期
在Thread提供的方法中,我们可以通过 isAlive() 、getState() 等方法查看对应线程的状态。一般情况下,这些方法已经够用了。在以下场景可以稍微做下文章:
- 需要监控任务何时开始、何时结束、获取任务的执行结果;
- 需要在任务执行的某个阶段做特定的事情(例如异常时做错误处理,结束时做资源清理、数据处理等);
思路
- 首先明确的是,需要对线程所执行的任务进行精确的阶段划分。所以需要一些常量来定义任务的生命周期,比如开始、执行中、完成、异常状态。
- 然后需要一个类来实现任务的各个阶段需要完成的事情(比如异常通知,资源释放),在此基础上可抽离出统一接口,可称为生命周期接口。
- 既然是在线程中执行任务,那么当然该在run方法里面来完成生命周期的回调。类比观察者模式,可将线程的run方法看做主题的状态改变方法,在run方法内出发生命周期接口的回调。
代码实现
任务
即线程需要执行的逻辑。
@FunctionalInterface
public interface Task<T> {
T call() throws Exception;
}
抽象主题
(此处额外定义 start 和 interrupt 方法是为了保持线程的一般使用习惯。)
public interface Observable {
//定义生命周期常量
enum Cycle {
STARTED, RUNNING, DONE, ERROR;
}
Cycle getCycle();
void start();
void interrupt();
}
观察者
生命周期接口
public interface TaskLifecycle<T> {
void onStart(Thread thread);
void onRunning(Thread thread);
void onFinish(Thread thread, T result);
void onError(Thread thread, Exception e);
}
具体主题
在多线程环境下,此处是即是Thread的子类,也同时是Observable 的实现类。
- 通常意义上的观察者模式,会在主题的状态变化方法内完成观察者类的回调。
- 对于多线程而言,则是在Thread的run方法完成观察者类回调。
public class ObservableThread<T> extends Thread implements Observable {
//持有观察者类的引用,即生命周期接口,在run方法触发回调
private final TaskLifecycle<T> lifecycle;
//线程需要执行的具体任务
private final Task<T> task;
//用来标识当前线程所处阶段
private Cycle cycle;
public ObservableThread(TaskLifecycle<T> lifecycle, Task<T> task) {
this.lifecycle = lifecycle;
this.task = task;
}
@Override
public final void run() {
//开始阶段的回调,设置线程为开始状态
this.update(Cycle.STARTED, null, null);
try {
//执行阶段的回调,设置线程为执行状态
this.update(Cycle.RUNNING, null, null);
T result = this.task.call();
//完成阶段的回调,设置线程为完成状态
this.update(Cycle.DONE, result, null);
} catch (Exception e) {
//异常阶段的回调,设置线程为异常状态
this.update(Cycle.ERROR, null, e);
}
}
@Override
public Cycle getCycle() {
return this.cycle;
}
private void update(Cycle cycle, T result, Exception e) {
this.cycle = cycle;
if (lifecycle == null) {
return;
}
try {
switch (cycle) {
case STARTED:
this.lifecycle.onStart(currentThread());
break;
case RUNNING:
this.lifecycle.onRunning(currentThread());
break;
case DONE:
this.lifecycle.onFinish(currentThread(), result);
break;
case ERROR:
this.lifecycle.onError(currentThread(), e);
break;
}
} catch (Exception exception) {
if (cycle == Cycle.ERROR) {
throw exception;
}
}
}
}
测试类
public class Test {
@SneakyThrows
public static void main(String[] args) {
//定义回调时需要完成的业务
TaskLifecycle<List<Integer>> lifecycle = new TaskLifecycle<List<Integer>>() {
@Override
public void onStart(Thread thread) {
//eg:资源准备,日志记录等
System.out.println(" now onStart ");
}
@Override
public void onRunning(Thread thread) {
//eg:日志记录
System.out.println(" now onRunning ");
}
@Override
public void onFinish(Thread thread, List<Integer> result) {
//eg:结果处理,资源回收
System.out.println(" now onFinish ,the result is " + result);
}
@Override
public void onError(Thread thread, Exception e) {
//eg:异常通知,异常记录
System.out.println(" now onError the exception is " + e);
}
};
Observable thread = new ObservableThread<>(lifecycle, () -> {
List<Integer> list = new ArrayList<>();
list.add(1);
TimeUnit.SECONDS.sleep(13);
return list;
});
thread.start();
TimeUnit.SECONDS.sleep(3L);
//测试手动中断线程(模拟异常情况)
thread.interrupt();
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
}
执行结果
now onStart
now onRunning
now onError the exception is java.lang.InterruptedException: sleep interrupted
总结
- 将ObservableThread的run方法修饰为final,是为了避免子类通过继承重写的方式,修改逻辑,这样会使通知机制遭受破坏;
- Observable接口内定义的start和run方法,是用来模糊Thread的API,即为了抽象主题接口的功能更明确,从设计上来说,用户应当将ObservableThread更多的视为被观察者,但是为了符合线程的使用习惯,本例便定义了这两个常用方法,如果需要其他功能,再增加即可。
- 另外本例实际上属于一主题、一观察者的情况。根据实际情况可以考虑让主题线程持有多个生命周期接口的引用,在run方法内进行遍历操作。
- 简单来说,其实就是在线程内完成了方法的回调。