在一系列关联的多任务的实时环境中
如果有一个任务发生失败
可能导致所有任务产生连锁反应
从而造成调度失控的局面
特别是对于核心控制设备尤其重要
为了解决这个问题
必须对每个任务进行实时监控
问题分析
在JAVA环境中一个任务一般是由一个独立线程来引导实现的独立线程可能调用一系列子线程如果在执行过程中某一个线程发生异常(产生的原因很多比如软件升级运行环境改变系统资抢占等)那么该线程就会停止运行直到下次任务重新被提交对于实时环境来说当前任务是失败的我们无法预测和完全避免异常的发生但是可以通过一些技术手段来跟蹤任务的状态从而及时发现问题并恢复正常减少损失
设计原理
对于一个实时任务而言执行效率是非常关键的这意味着不可能考虑用非常复杂的方式来实现任务监控即使这样可以做的比较完善同时监控代码本身也会引入一些异常这就要求监控程序必须简单可靠能够发现大多数问题并能及时处理
一个可能的简单实现
我们对每个任务加上一个监控的壳调度程序调用这个壳来完成对具体任务的引导和监控相当于每个任务具有自治能力这样做的好处有
分散控制不需要修改调度主体程序不增加调度过程的复杂度
控制灵活安全性高对于不同任务可定义不同控制方式和控制参数封装在任务内部灵活性好对个别任务控制代码的修改不会影响其他任务即任务控制实现松藕合避免错误扩散适合团队开发
维护简单升级方便对于新的任务加入也无需改变原来调度程序的结构只需修改任务表及相关参数这样在性能提高的同时也简化了软件升级过程中的代码维护量
设计实现
每个线程理论上有四种状态
new 线程对象已经创建
但尚未启动
不可运行 runnable 一旦有时间分片机制有空闲的CPU周期
线程立即开始运行 dead 从run()方法退出后
一个线程即消亡 blocked 线程可运行
但有某种东西阻碍了它
调度机制不给它分配任何CPU时间
直到它进入runnable状态
在实际操作中为了便于描述我们重新规定了线程的状态
Actived 线程已被激活
处于运行状态 Sleeping 线程完成一个特定任务后退出
进入休眠状态 Dead 线程运行过程中发生异常
终止运行
处于死亡状态
根据上述理论我们只需要对Actived状态的线程进行监控也只有对Actived状态监控才有意义这是对监控模块做出逻辑简化那么如何实现监控模块对具体任务的监控呢?
对具体任务的监控方式有多种根据任务的不同需要采用不同的监控代码但是在结构上基本相同这和类的重载概念有点相似本文附有一个简单的例子
A任务每秒执行一个简单的代数运算 j = / i并打印结果我们故意在其中设置了一个异常陷阱使得执行过程中出现一次被除的算术异常下面结合这个例子讲述监控原理
为了监控A我们设计了一个监控线程MM中定义了一些关键逻辑变量
Keepchecking 持续监控标志 laststatus 保存上次监控状态 maydeadtimes 监控线程可能死亡的计数器 maydeadtimeout 定义判断线程死亡的边界条件 deadtimes 监控线程死亡次数的计数器 deadtimeout 定义判断线程不正常的边界条件
为了适应监控在A任务中相应增加一些可以被监控的状态和行为
dead 死状态标志 dead = !dead; 改变状态
整个监控就是围绕这些状态标志和行为展开的A任务定期修改自己的dead标志dead是一个布尔变量理论上只要A没有死那么dead肯定是周期变化的(和心跳概念差不多)M需要做的就是监控dead的变化为了避免一些偶然因素导致的误判我们在M中设置了一些计数器和边界值(maydeadtimes和maydeadtimeout)当M发现A的可能死亡次数超过一定限制后判断A已死亡不在继续等待了作为实时处理首先注销A的实例然后重新启动A(和我们计算机死机的复位很像)然后进入新一轮监控
如果是因为系统偶然因素导致A死亡那么在随后的新的任务启动过程中一般可以顺利完成但是万一由于环境参数改变或软件升级存在版本缺陷A可能始终会产生异常那么M是否需要耐心地监控下去呢?一个形象的例子是如果你连续次开机都失败你是否会怀疑机器有问题?当然你会那么M也应该会
为了对A任务重复多次死亡有一个统计M中又引入了另外对计数器和边界值(deadtimes和deadtimeout)和你开计算机的过程一样如果连续n次都发现A有问题可以基本肯定不是由于偶然因素引起的需要对A的代码或系统的环境进行检查M会发出告警通知必须要对A进行审查了然后清空A自己自动安全退出如果在核心调度程序中设置一个标志接受M们的告警就可以有足够理由终止其他任务的执行可以看见在A任务发生异常期间M承担了核心调度程序的维护功能特别是当任务数量比较多的情况核心调度程序只能采用排队方式处理任务异常而且由于处理异常的复杂程度不同无法保证对多任务异常的实时处理
还要考虑正常情况下A和M的关系核心调度程序通过M启动A任务后M处于持续监控状态当A正常结束任务后A需要通知M结束监控这样当A进入休眠状态后M也不会占用内存空间提高了系统资源的利用率
通过以上描述可以看到上述监控思想具有清晰的概念和可操作性占用资源少为保证系统连续稳定运行创造了条件
具体代码实现附后
运行结果如下
异常情况 正常情况 i=: status=true
M read A status = true
i=: status=false
M read A status = false
i=: status=true
M read A status = true
A become Exception!
M read A status = true
M read A status = true
M read A status = true
A is deaded!
M is restarting A!
____________________________
i=: status=false
M read A status = false
i=: status=true
M read A status = true
i=: status=false
M read A status = false
A become Exception!
M read A status = false
M read A status = false
M read A status = false
A is deaded!
M is restarting A!
____________________________
i=: status=true
M read A status = true
i=: status=false
M read A status = false
i=: status=true
M read A status = true
A become Exception!
M read A status = true
M read A status = true
M read A status = true
Alert! A is unstable M will stop it
(结束)
i=: status=true
M read A status = true
i=: status=false
M read A status = false
i=: status=true
M read A status = true
i=: status=false
M read A status = false
i=: status=true
M read A status = true
A is Ending M
M read A status = true
(结束)
结束语
通过给制定任务线程增加监控线程可以很好地解决实时多任务环境下的安全监控问题同时避免了核心调度线程事务过分复杂的问题实践证明该方法复杂度小占用资源少运行可靠适合复杂条件下的多任务环境
源代码
// 核心调度程序public class mythread { public mythread() { } public static void main(String[] args) { M m = new M(); }}// A 任务线程class A extends Thread { public static boolean dead = false; M m; A(M m){ m = m; start(); } public void run(){ try{ for(int i=;i<= ;i++){ int j=/i; // 人为设置过程中陷阱 dead = !dead; // 活动状态 Systemoutprintln(i= + i + : status= + dead); try{ sleep(); } catch(InterruptedException ie){ Systemoutprintln(A is Interrupted!); } } mKeepchecking = false; //A 正常结束后关闭监控线程 Systemoutprintln(A is Ending M); } catch(Exception e){ Systemoutprintln(A become Exception!); } }}// 监控线程class M extends Thread{ public static boolean Keepchecking = true; // 持续监控标志 boolean laststatus; //保存上次监控状态 int maydeadtimes = ; //监控线程可能死亡的计数器 int maydeadtimeout = ;//定义判断线程死亡的边界条件 int deadtimes = ; //监控线程死亡次数的计数器 int deadtimeout = ; //定义判断线程不正常的边界条件 A a; M(){start();} public void run(){ schedule(); while(Keepchecking){ laststatus = adead; try{ sleep(); } catch(InterruptedException e){ Systemoutprintln(M is Interrupted!); } Systemoutprintln(M read A status = + adead); if(laststatus == adead ){ if(++maydeadtimes >= maydeadtimeout){ if(++deadtimes >= deadtimeout){ Systemoutprintln(Alert! A is unstable M will stop it); a = null; break; } else{ Systemoutprintln( A is deaded!); schedule(); Systemoutprintln(M is restarting A!\n____________________________\n); } } } else{ maydeadtimes = ; } } } private void schedule(){ A a = new A(this); }}