监控任务的生命周期:多线程下的观察者模式

本文内容总结于《Java高并发编程详解 多线程与架构设计》

观察者模式回顾

关于具体实现,本文不再做相关介绍,可以查看链接->观察者模式案例

观察者模式的核心可以总结为:

  • 主题持有观察者的引用,可以在主题状态变化时通知观察者;
  • 主题内部维护了一个集合保存所有观察者,用以特定通知或广播;

总结的再精炼一点,观察者模式其实就是回调机制的体现和延伸而已。

实现案例:监控任务的生命周期

在Thread提供的方法中,我们可以通过 isAlive() 、getState() 等方法查看对应线程的状态。一般情况下,这些方法已经够用了。在以下场景可以稍微做下文章:

  1. 需要监控任务何时开始、何时结束、获取任务的执行结果;
  2. 需要在任务执行的某个阶段做特定的事情(例如异常时做错误处理,结束时做资源清理、数据处理等);

思路

  1. 首先明确的是,需要对线程所执行的任务进行精确的阶段划分。所以需要一些常量来定义任务的生命周期,比如开始、执行中、完成、异常状态。
  2. 然后需要一个类来实现任务的各个阶段需要完成的事情(比如异常通知,资源释放),在此基础上可抽离出统一接口,可称为生命周期接口。
  3. 既然是在线程中执行任务,那么当然该在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方法内进行遍历操作。
  • 简单来说,其实就是在线程内完成了方法的回调。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值