多线程任务生命周期管理:观察者模式的高级玩法

目前有什么问题?

在银行系统的批量开发中,我们通常会实现多个后台任务(Task)。这类任务的典型执行流程是:由shell脚本调用Java程序,传入任务ID作为参数;Java主进程根据任务ID启动新的线程(或提交给线程池),执行对应的任务逻辑。

采用Task的主要场景是:当业务逻辑复杂度较高时,使用纯shell脚本开发维护困难,因此借助Java来实现核心处理。

实现Task时常见的需求包括:

  1. 任务启动/完成时记录日志和数据库状态
  2. 异常发生时发送邮件告警
  3. 根据项目需求扩展通知机制,如短信提醒或消息队列通知下游系统

如何更优雅地处理上述需求,就是我们本次要讨论的问题

如何优化?

好,这个需求你想到了什么设计模式?

3,2,1

那必须是观察者模式啊!没有想到的V我50请我吃疯狂星期四

什么是观察者模式?

说是观察者,其实我认为叫监视者更合适!观察者类似于一个特务机构

讲一个故事,张作霖和日本特务机构,当年的张大帅被日本人拿炸弹炸死,这张大帅身边一定有日本的特务,这个特务就是每天观察大帅的一举一动,即时汇报给日本驻扎东北的特务头子,然后特务头子,就跟据张大帅的一举一动,做出不同的安排。

比如:张大帅发展大炮,安插在张大帅身边的小特务知道后,就去汇报给特务头子,特务头子就做出反应,不给张作霖钢铁,日本不卖给他钢铁了!

上个例子中,张大帅就是被观察者(主题),小特务属于是眼线,在观察者模式中也会有眼线的存在,大特务属于观察者,观察者观察到现象之后会做出一些动作。

手写观察者模式

先给个uml类图给各位看官看看~

首先先来个被观察的接口,我们这里叫做主题

interface Subject {
    // 注册观察者(安插眼线)
    void registerObserver(Observer observer);
    // 移除观察者(移除眼线眼线)
    void removeObserver(Observer observer);
    //通知观察者
    void notifyObservers();
}

观察者接口

interface Observer {
    // 观察者观察到现象后执行一些逻辑,
    // 可以理解为特务头子收到信息后进行的活动,比如例子中的不出售给张大帅钢铁
    void update(String message);
}

观察者与主题的实现

主题
class ConcreteSubject implements Subject {
    // 观察者列表
    private List<Observer> observers = new ArrayList<>();
    private String state;

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }

    // 业务方法 - 状态改变时通知观察者
    public void setState(String newState) {
        this.state = newState;
        System.out.println("Subject state changed to: " + newState);
        notifyObservers();
    }
}




// 具体观察者A
class ConcreteObserverA implements Observer {
    @Override
    public void update(String message) {
        // 这里可以添加具体的响应逻辑,如更新数据库表的状态
        System.out.println("Observer A 我要炸铁路-->" + message);

    }
}

// 具体观察者B
class ConcreteObserverB implements Observer {
    @Override
    public void update(String message) {
        // 这里可以添加具体的响应逻辑 如发送短信
        System.out.println("Observer B 我要限制钢铁出口-->" + message);
    }
}

接下来是一个使用实例demo

// 使用示例
public class ObserverPatternDemo {
        public static void main(String[] args) {
        // 创建主题
        ConcreteSubject subject = new ConcreteSubject();

        // 创建观察者
        Observer observerA = new ConcreteObserverA();
        Observer observerB = new ConcreteObserverB();

        // 注册观察者
        subject.registerObserver(observerA);
        subject.registerObserver(observerB);

        // 改变主题状态(自动通知所有观察者)
        subject.setState("张大帅发展大炮");
        System.out.println("=============");

        // 移除一个观察者
        subject.removeObserver(observerA);

        // 再次改变状态
        subject.setState("张大帅发展铁路运输");
    }
}

如何将观察者与线程完美融合?

我们先想一下线程是如何使用的?这里只讨论继承Thread类实现多线程的方式

学过Java的都知道哈,继承Thread类后只需要重写run 方法,然后点击start,就可以使用一个新的线程去完成run方法中的任务。

那么我们可不可以这样呢?

把在run方法执行前,执行后都加上主题中的notifyObservers方法,通知观察者,线程正在准备执行run方法、run方法执行有异常、run方法执行结束。

说干就干,请看代码

主题接口

interface Subject<T> {

    enum Cycle {
        STARTED, RUNNING, DONE, ERROR
    }

    // 定义启动线程的方法,主要作用是为了屏蔽Thread的其他方法
    void start();

    // 定义线程的打断方法,作用与start方法一样,也是为了屏蔽Thread的其他方法
    void interrupt();

    // 获取当前任务的生命周期状态
    Cycle getCycle();

    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers(Cycle cycle, T result, Exception e);
}
// 观察者接口
interface Observer<T> {
    void onStart(Thread thread);

    // 任务正在运行时会触发onRunning方法
    void onRunning(Thread thread);

    // 任务运行结束时会触发onFinish方法,其中result是任务执行结束后的结果
    void onFinish(Thread thread, T result);

    // 任务执行报错时会触发onError方法
    void onError(Thread thread, Exception e);
}

主题接口实现

class ThreadSubject<T> extends Thread implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private Cycle cycle;
    private final Task<T> task;

    public ThreadSubject(Observer<T> observer, Task<T> task) {
        super();
        // Task不允许为null
        if (task == null)
            throw new IllegalArgumentException("The task is required.");
        this.registerObserver(observer);
        this.task = task;
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public final void run() {
        // 在执行线程逻辑单元的时候,分别触发相应的事件
        this.notifyObservers(Cycle.STARTED, null, null);
        try {
            this.notifyObservers(Cycle.RUNNING, null, null);

            T result = this.task.call();
            this.notifyObservers(Cycle.DONE, result, null);
        } catch (Exception e) {
            this.notifyObservers(Cycle.ERROR, null, e);
        }
    }

    @Override
    public void notifyObservers(Cycle cycle, Object result, Exception e) {
        this.cycle = cycle;
        if (observers == null || observers.isEmpty())
            return;
        try {
            switch (cycle) {
                case STARTED:
                    // 使用lambda循环通知观察者
                    observers.forEach(observer -> observer.onStart(currentThread()));
                    break;
                case RUNNING:
                    observers.forEach(observer -> observer.onRunning(currentThread()));
                    break;
                case DONE:
                    observers.forEach(observer -> observer.onFinish(currentThread(), result));
                    break;
                case ERROR:
                    observers.forEach(observer -> observer.onError(currentThread(), e));
                    break;
            }
        } catch (Exception ex) {
            if (cycle == Cycle.ERROR) {
                throw ex;
            }
        }
    }

    @Override
    public Cycle getCycle() {
        return this.cycle;
    }

}

观察者A

// 具体观察者A
class ConcreteObserverA implements Observer {

    @Override
    public void onStart(Thread thread) {
        System.out.println("观察者A:开始执行");
    }

    @Override
    public void onRunning(Thread thread) {
        System.out.println("观察者A:正在执行");
    }

    @Override
    public void onFinish(Thread thread, Object result) {
        System.out.println("观察者A:执行结束");
    }

    @Override
    public void onError(Thread thread, Exception e) {
        System.out.println("观察者A:执行异常");
    }
}

FunctionalInterface的task接口

@FunctionalInterface
public interface Task<T>
{
    //任务执行接口,该接口允许有返回值
    T call() throws InterruptedException;
}

测试demo

public class ObserverPatternDemo {
    public static void main(String[] args) throws InterruptedException {

        Task<String> task = () -> {
            System.out.println("任务执行中...");
            for (int i = 1; i <= 5; i++) {
                TimeUnit.SECONDS.sleep(1);
                System.out.println("执行" + (i / 5.0) * 100 + "%。。。。。。。");
            }
            System.out.println("任务执行结束。。。。。。。。");
            return "Success";
        };

        ConcreteObserverA concreteObserverA = new ConcreteObserverA();
        // 创建主题
        ThreadSubject subject = new ThreadSubject(concreteObserverA,task);


        Observer observerB = new ConcreteObserverB();
        subject.registerObserver(observerB);
        subject.start();
        // 等待上述线程执行结束
        subject.join();
        System.out.println("======================");

        // 移除一个观察者
        subject.removeObserver(concreteObserverA);
        ThreadSubject newSubject = new ThreadSubject(concreteObserverA,task);
        newSubject.start();

    }
}

执行截图

好的我们来总结一下改动点

  1. 使用了函数式接口来定义任务,任务的执行作为主题线程的run方法的主要逻辑
  2. 在任务执行的不同阶段(启动、运行中、完成、出错)主动调用 notifyObservers(…) 方法通知所有观察者;
  3. 使用了枚举类型来表示的任务的不同状态,不同的状态会触发观察的相对应的方法
  4. ThreadSubject重写父类的run方法,其修饰为final,不允许子类再次对其进行重写

还有什么问题?

还有什么问题呢?

  1. 观察者的列表呢不是一个安全的,我们需要更改一下CopyOnWriteArrayList 就很合适,或者自己加上synchronized 关键字
  2. 观察者接口的各个方法 如onFinish,onRunning… 这写方法在实现的时候一定要注意异常处理和超时控制,不然会导致notifyObservers 方法跟着一起失败,一起出现异常,如果其中一个观察者出现异常,那么后续的观察者将收不到消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码出优雅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值