引言

  在之前的博客中我们知道,Thread提供了很多可获取的状态,以及判断是否alive的方法,但是这些方法都是线程本身提供的,在Runnable运行的过程中所处的状态是无法直接获取到的到,例如什么时候开始的?什么时候结束的?都需要另外的方法进行获取。有的时候为了监听线程的状态不得不再其中设置共享变量。但是在多线程的情况下设置共享变量导致资源竞争从而增加了其他数据不一致的安全隐患。

  在学习设计模式的时候我们假设了如下的场景,当一个某个对象的变化要被其他线程或者对象知道的时候,我们就是使用观察者模式。在这里,对于线程的监控也可以使用观察者模式。当然观察者模式需要有观察事件的来源,也就是导致事件变化的源头。在多线程的情况下很明显Thread类就作为线程事件改变的源头,这个源头在整个生命周期中事件的变化要通知事件接收方,也就是说需要一个观察者来监视事件的变化。下面就通过一个简单的例子来看一下关于这些模式的使用

定义一个接口
public interface Observable {//任务生命周期的枚举类型enum Cycle{STARED,RUNNING,DONE,ERROR}//获取当前任务生命周期状态Cycle getCycle();//定义启动线程的方法,主要是为了隐藏Thread的其他方法void start();//定义线程终止的方法,作用和start方法一样,避免Thread的方法void interrupt();
}

  这个接口的主要作用是暴露给调用者使用。设置了四个枚举类型分别代表当前任务的执行生命周期。

  • getCycle()方法用于获取当前的任务在生命周期的哪个阶段
  • start()方法主要是为了屏蔽Thread的其他的方法,可以通过这个方法来启动整个线程
  • interrupt()方法的作用和start的方法作用相同
TaskLifecycle接口定义
public interface TaskLifecycle<T> {//任务启动时会触发onStart方法void onStart(Thread thread);//任务运行的时候触发onRunning方法void onRunning(Thread thread);//任务结束的时候会触发onFinish方法,其中result是任务执行完成之后的结果void onFinish(Thread thread,T result);//任务出错的时候会触发onError方法void onError(Thread thread,Exception e);//生命周期接口的空实现 Adapterclass EmptyLifecycle<T> implements TaskLifecycle<T>{@Overridepublic void onStart(Thread thread) {}@Overridepublic void onRunning(Thread thread) {}@Overridepublic void onFinish(Thread thread, T result) {}@Overridepublic void onError(Thread thread, Exception e) {}}
}

  这个接口主要是定义了在线程执行过程中会被触发的那些接口,其中我们看到有一个空实现内部类。其中

  • onStart()方法表示任务开始执行的时候会被调用的方法
  • onRunning()方法表示在任务执行过程中会被调用的方法,这个也是根据线程的生命周期来定义的。
  • onFinish()方法表示整个任务顺利执行完成之后会调用的方法。
  • onError()方法表示在运行的任何时间内出现异常都会被这个方法回调。
Task函数接口定义
@FunctionalInterface
public interface Task<T> {//任务执行接口,该接口允许有返回值T call();
}

  由于需要对线程中执行的任务做监听,需要实现一个回调方法,将结果进行返回所以实现了这样的一个接口。

ObservableThread类实现
public class ObservableThread<T> extends Thread implements Observable {private final TaskLifecycle<T> lifecycle;private final Task<T> task;private Cycle cycle;public ObservableThread(Task<T> task) {this(new TaskLifecycle.EmptyLifecycle<>(),task);}public ObservableThread(TaskLifecycle<T> lifecycle,Task<T> task) {super();if (task==null){throw new IllegalArgumentException("The task is required.");}this.lifecycle = lifecycle;this.task = task;}public final void run(){this.update(Cycle.STARED,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);}}private void update(Cycle cycle,T result,Exception e){this.cycle = cycle;if (lifecycle==null){return;}try{switch (cycle){case STARED: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 ex){if (cycle==Cycle.ERROR){throw ex;}}}@Overridepublic Cycle getCycle() {return this.cycle;}
}

  在这里作为整个监控任务的核心,整个类继承了Thread类和Observable接口,并且在构造期间需要传入任务的具体实现。然后重写了父类的run()方法并且将其设置为final,我们知道final表示整个方法不会再被重写。所以说在生命周期的任何时间内每发生一个事件都要低啊用update方法,这个方法就是需要通知监听者任务执行过程中的变化。其实这里的原理和call()方法的原理是一样的,当任务状态发生改变的时候就要让观察者及时更新。

总结

  通过实现我们会发现给他的实现其实与Thread的实现并没有多大的区别,只是ObservableThread作为一个泛型存在,因为返回值为Void也就表示并不在乎他的返回值是什么。

public class Main {public static void main(String[] args) {Observable observableThread = new ObservableThread<>(()->{try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("finish done");return null;});observableThread.start();}
}

  在下面的实现中我们会发现其实对于EmptyLifecycle也是没有返回值。但是通过onFinish方法将最后的结果进行的返回。

public class Test {public static void main(String[] args) {final TaskLifecycle<String> lifecycle = new TaskLifecycle.EmptyLifecycle<String>() {@Overridepublic void onFinish(Thread thread, String result) {System.out.println("The result is " + result);}};Observable observableThread = new ObservableThread<>(lifecycle,()->{try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(" finished done.");return "Hello Observer";});observableThread.start();}}

关键

  在上面的实现过程中作为核心的一点就是将Thread的方法进行了封装。首先屏蔽了Thread的API,其次封装了run方法让其不能被继承,为了实现状态的及时通知提供了两个方法call和update。但是最主要的就是离不开封装和展示。封装了不愿意被用户看到的,展示了想观察者所想要的。Observable类作为观察者最想看到的东西暴露出来。在后续博客中讲解分享设计模式的时候还会对这些内容进行总结,敬请期待!

Java高并发编程详解系列-线程生命周期观察者相关推荐

  1. Java高并发编程详解系列-线程上下文设计模式及ThreadLocal详解

    导语   在之前的分享中提到过一个概念就是线程之间的通信,都知道在线程之间的通信是一件很消耗资源的事情.但是又不得不去做的一件事情.为了保证多线程线程安全就必须进行线程之间的通信,保证每个线程获取到的 ...

  2. Java高并发编程详解系列-线程池原理自定义线程池

    之前博客的所有内容是对单个线程的操作,例如有Thread和Runnable的使用以及ThreadGroup等的使用,但是对于在有些场景下我们需要管理很多的线程,而对于这些线程的管理有一个统一的管理工具 ...

  3. Java高并发编程详解系列-线程安全数据同步

    在多线程中最为复杂和最为重要的就是线程安全.多个线程访问同一个对象的时候会导致线程安全问题.通过加锁可以避免这种问题.但是在串行执行的过程中又不用考虑线程安全问题,而使用串行程序效率低没有办法将CPU ...

  4. Java高并发编程详解系列-线程上下文类加载

    前面的分享中提到的最多的概念就是关于类加载器的概念,但是当我们查看Thread源码的时候会发现如下的两个方法,这两个方法就是获取或者设置线程的上下文类加载器的方法,那么为什么要设置这两个方法呢?这个就 ...

  5. Java高并发编程详解系列-线程通信

      进程间的通信,又被称为是进程内部的通信,我们都知道每个进程中有多个线程在执行,多个线程要互斥的访问共享资源的时候会发送对应的等待信号或者是唤醒线程执行等信号.那么这些信号背后还有什么样的技术支持呢 ...

  6. Java高并发编程详解系列-线程异常处理

    前面的博客中主要描述的关于线程的概念,通过源码分析了解线程的基本操作方式,但是如何在线程运行期间获取异常信息呢?这就要使用到一个Hook线程了 线程运行时的异常   在Thread类中,关于线程运行时 ...

  7. Java高并发编程详解系列-Java线程入门

    根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面.   首先介绍一下这个系列的东西是什么,这个系列自己 ...

  8. Java高并发编程详解系列-7种单例模式

    引言 在之前的文章中从技术以及源代码的层面上分析了关于Java高并发的解决方式.这篇博客主要介绍关于单例设计模式.关于单例设计模式大家应该不会陌生,作为GoF23中设计模式中最为基础的设计模式,实现起 ...

  9. Java高并发编程详解系列-Future设计模式

    导语   假设,在一个使用场景中有一个任务需要执行比较长的时间,通常需要等待任务执行结束之后或者是中途出错之后才能返回结果.在这个期间调用者只能等待,对于这个结果Future设计模式提供了一种凭据式的 ...

最新文章

  1. ARM汇编伪指令介绍(全集)
  2. java socket 自动重连_socket 如何判断远端服务器的连接状态?连接断开,需重连...
  3. 「第五篇」全国电子设计竞赛-电源题设计方案总结
  4. 为什么要做稀疏编码_为什么我每天都要编码一年,所以我也学到了什么,以及如何做。...
  5. mysql first value_开窗函数 First_Value 和 Last_Value
  6. linux motion 分辨率,gtf 来调整Linux系统中分辩率问题分享
  7. clickhouse 的mysql表引擎
  8. android文件操作和SDCard卡操作
  9. ngrok 通过外网链接映射到本地机器,支持http,https
  10. 电脑横机服务器维修,三招搞定电脑横机维修
  11. 极限压缩----压缩至原视频的五分之一
  12. windows运行中自定义命令创建/自定义bat文件创建
  13. 指数加权平均(EMA)理解
  14. 2022年9月青少年软件编程(图形化)等级考试试卷--三级--数星星
  15. 华为 两条线路负载均衡_华为无线AP4050DN接入点高可靠性,高安全性!
  16. 2022年天梯赛比赛真题,L1基础题,C语言,没有算法的那种
  17. 杨超越杯编程大赛登上 GitHub,程序员为追星都开发了什么?
  18. 超级本将成为平板电脑杀手
  19. 2分钟让你搞懂 grid-template-areas
  20. G- Wall Game

热门文章

  1. 安卓程序员都懂:如何用Espresso对UI界面测试?
  2. linux C 编译时手动链接遇到的问题(未解决)
  3. 应用 memcached 提升站点性能
  4. 网管,请别随意关闭默认共享
  5. 第六章 核心API (二)
  6. 计算机硬件基础与linux发展史
  7. Swagger2接口注释参数使用数组
  8. 如何看待阿里云加入Linux基金会金牌会员?
  9. 轨迹系列2——一种基于中值滤波的轨迹纠偏方法和几点思考
  10. (原+转)使用opencv的DFT计算卷积