多线程设计模式:监控任务的声明周期(观察者模式)

1 场景描述

虽然Thread为我们提供了可获取状态,以及判断是否alive的方法,但是这些方法均是针对线程本身的,而我们提交的任务Runnable在运行过程中所处的状态如何是无法直接获得的,比如它什么时候开始,什么时候结束,最不好的一种体验是无法获得Runnable任务执行后的结果。一般情况下想要获得最终结果,我们不得不为Thread或者Runnable传入共享变量,但是在多线程的情况下,共享变量将导致资源的竞争从而增加了数据不一致性的安全隐患。

2 观察者模式

被观察者发生变化的时候,通知观察者,观察者就监听到这一变化可做成相应的反应

当某个对象发生状态改变需要通知第三方的时候,观察者模式就特别适合胜任这样的工作。观察者模式需要有事件源,也就是引发状态改变的源头,很明显Thread负责执行任务的逻辑单元,它最清楚整个过程的始末周期,而事件的接收者则是通知接受者一方,严格意义上的观察者模式是需要Observer(观察者)的集合的,我们在这里不需要完全遵守这样的规则,只需将执行任务的每一个阶段都通知给观察者即可

核心角色:

  • 被观察者:Observerable/Subject
  • 观察者:Observer

3 实现

3.1 接口定义

3.1.1 Observable接口定义(被观察者)

Observable接口:可被观察,该接口主要是暴露给调用者使用的,其中四个枚举类型分别代表了当前任务执行生命周期的各个阶段
观察者模式的核心角色,被观察对象需要实现该接口,这个场景下,我们的线程是我们观察的目标,所以需要实现该接口。

public interface Observable {

   /**
    * 任务生命周期的枚举类型
    */
   enum Cycle{
       STARTED, RUNNING, DONE, ERROR
   }

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

   /**
    * 可通过Observable的start对线程进行启动
    * 定义启动线程的方法,主要作用是为了屏蔽Thread的其他方法
    * 提现面向对象的封装性
    */
   void start();


   /**
    * 可通过Observable的interrupt对当前线程进行中断
    * 定义线程的打断方法,作用与start方法一样,也是为了屏蔽Thread的其他方法
    */
   void interrupt();
}
3.1.2 TaskLifecycle接口定义(观察者)

这个接口就是定义,线程开始执行,运行中,执行结束,执行出现异常的回调接口

public interface TaskLifecycle<T> {

    /**
     * 任务启动时候触发的方法
     * @param thread
     */
    void onStart(Thread thread);

    /**
     * 任务正在运行时候会触发的方法
     * 由于我们针对的是任务的生命周期,不同于线程生命周期中的RUNNING状态,
     * 如果当前线程进入了休眠或者阻塞,那么任务都是running状态。
     * @param thread
     */
    void onRunning(Thread thread);

    /**
     * 任务运行结束时,会触发发的结果
     * @param thread
     * @param result result是任务执行结束后的结果
     */
    void onFinish(Thread thread,T result);

    /**
     * 任务执行报错时会触发onError方法
     * @param thread
     * @param e 发生的异常
     */
    void onError(Thread thread, Exception e);


    /**
     * 空实现
     * @param <T>
     */
    class EmptyLifecycle<T> implements TaskLifecycle<T> {

        @Override
        public void onStart(Thread thread) {
            //do nothing
        }

        @Override
        public void onRunning(Thread thread) {
            //do nothing
        }

        @Override
        public void onFinish(Thread thread, T result) {
            //do nothing
        }

        @Override
        public void onError(Thread thread, Exception e) {
            //do nothing
        }
    }
}

在内部提供了一个空实现,这样使用者如果不自己实现,默认就是这个空实现,使用者并不会感知到其存在,就可以保持对Thread类的使用习惯

3.1.3 Task函数接口定义

该接口替代jdk原生Runnable接口,由于我们需要对线程中的任务执行增加可观察的能力,并且需要获得最后的运行结果,因此Runnable接口在可观察的线程中将不再使用,取而代之的是Task接口,其作用与Runnable类似,主要用于承载任务的逻辑执行单元。

@FunctionalInterface
public interface Task<T> {

    /**
     * 任务执行接口,返回任务结果, 替代的就是Runnable接口的run方法,
     * 区别就是这个方法支持返回值
     * @return
     */
    T call();
}

3.2 接口实现

3.2.1 ObservableThread实现

因为Thread类是jdk,我们不可能让他去实现Observable接口,所以只好自己定义一个类,来实现Observable接口 ,
但是又需要它具有Thread的作用,所以ObservableThread还需要继承Thread。

并且需要重写run方法,将其修饰为final类型,不允许子类再次对其进行重写,重写主要是为了能够让其具有监控能力,也就是能够在每个节点通知到我们的观察者(TaskLifecycle接口的实现)

并且在线程执行的过程中需要通知观察者,所以观察者(ObservableThread)需要持有被观察者(TaskLifecycle接口的实现)

/**
 * @author wyaoyao
 * @date 2021/4/5 11:56
 * 可观察的线程
 * 要具备两个能力:
 * 1. 得是线程:所以要继承Thread
 * 2. 要可观察,提供观察能力,所以需要实现Observable接口
 */
public class ObservableThread<T> extends Thread implements Observable {

    private final TaskLifecycle<T> lifecycle;

    /**
     * 任务,可理解为Runnable接口
     */
    private final Task<T> task;

    /**
     * 当前线程执行到的节点
     */
    private Cycle cycle;

    public ObservableThread(TaskLifecycle<T> lifecycle, Task<T> task) {
        if (Objects.isNull(task)) {
            // task 不能是空
            throw new IllegalArgumentException("The task is required but now is null");
        }
        this.lifecycle = lifecycle;
        this.task = task;
    }


    public ObservableThread(Task<T> task) {
        // 指定Task的实现,默认情况下使用EmptyLifecycle
        this(new TaskLifecycle.EmptyLifecycle<>(), task);
    }

    /**
     * 重写Thread的run方法
     * 使用final修饰,不能被重写
     */
    @Override
    public final void run() {
        // 任务执行的时候,更新状态为开始
        this.update(Cycle.STARTED, null, null);
        try {
            // 开始执行任务
            this.update(Cycle.RUNNING, null, null);
            // 执行任务
            T result = task.call();
            // 执行结束
            this.update(Cycle.DONE, result, null);
        } catch (Exception e) {
            // 如果出现异常,执行异常回调
            this.update(Cycle.ERROR, null, e);
        }
    }

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

    /**
     * 更新状态,并回调每个状态对应的函数
     *
     * @param cycle
     * @param result
     * @param e
     */
    private void update(Cycle cycle, T result, Exception e) {
        this.cycle = cycle;
        if (this.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;
                default:
                    break;
            }
        } catch (Exception exception) {
            // 在响应某个事件的过程中出现了意外,则会导致任务的正常执行受到影响,因此需要进行异常捕获,
            // 并忽略这些异常信息以保证TaskLifecycle的实现不影响任务的正确执行,但是如果任务执行过程中出现错误并且抛出了异常,
            // 那么update方法就不能忽略该异常,需要继续抛出异常,保持与call方法同样的意图。
            if (cycle == Cycle.ERROR) {
                throw exception;
            }
        }

    }
}

4 测试

public class Client {

    public static void main(String[] args) throws InterruptedException {
        // 1 定义观察者
        TaskLifecycle<String> taskLifecycle = new TaskLifecycle<String>() {

            @Override
            public void onStart(Thread thread) {
                System.out.println("thread has start");
            }

            @Override
            public void onRunning(Thread thread) {
                System.out.println("thread will running");
            }

            @Override
            public void onFinish(Thread thread, String result) {
                System.out.println("thread has finished");
            }

            @Override
            public void onError(Thread thread, Exception e) {
                System.out.println("thread has error");
            }
        };

        ObservableThread<String> observableThread = new ObservableThread(taskLifecycle,() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 模拟异常
            // int a = 1/0;
            return "hello ObservableThread";
        });

        // 启动线程
        observableThread.start();
        System.out.println("当前线程的状态: " + observableThread.getCycle());
        TimeUnit.SECONDS.sleep(1);
        System.out.println("当前线程的状态: " + observableThread.getCycle());
        TimeUnit.SECONDS.sleep(5);
        System.out.println("当前线程的状态: " + observableThread.getCycle());
    }
}

5 总结

  • 在接口Observable中定义与Thread同样的方法用于屏蔽Thread的其他API,在使用的过程中使用Observable声明ObservableThread的类型,如果使用者还想知道更多的关于Thread的API,只需要在Observable接口中增加即可。
  • 将ObservableThread中的run方法修饰为final,或者将ObservableThread类修饰为final,防止子类继承重写,导致整个生命周期的监控失效,我们都知道,任务的逻辑执行单元是存在于run方法之中的,而在ObservableThread中我们摒弃了这一点,让它专门监控业务执行单元的生命周期,而将真正的业务逻辑执行单元交给了一个可返回计算结果的接口Task。
  • ObservableThread本身的run方法充当了事件源的发起者,而TaskLifecycle则扮演了事件回调的响应者。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值