java 线程管理框架_实现 Java 多线程并发控制框架
所面临的问题
图 1. 线程场景
这幅图中节点代表一个 single Thread,边代表执行的步骤。
整幅图代表的意思是,ROOT 线程执行完毕后执行 T1 线程,T1 执行完毕后并发的执行 T2 和 T3。而从 T2 和 T3 指向 T4 的两条边表示的是 T4 必须等 T2 和 T3 都执行完毕以后才能开始执行。剩下的步骤以此类推,直到 END 作为整个过程的结束。当然,这只是个简略的示意图,可能面对的一个线程场景会有上百个线程。还有,你可以观察到这整个场景只有一个入口点和一个出口点,这意味着什么?在下文中为你解释。
这其中涉及到了 Java 线程的同步互斥机制。例如如何让 T1 在 T2 和 T3 之前运行,如何让 T2 和 T3 都执行完毕之后开启 T4 线程。
模型的描述
如何来描述图 1 中所示的场景呢?可以采用 XML 的格式来描述我们的模型。我定义一个“Thread” element 来表示线程。
其中 ID 是线程的唯一标识符,PRETHREAD 便是该线程的直接先决线程的ID,每个线程 ID 之间用逗号隔开。
在 Thread 这个 element 里面可以加入你想要该线程执行任务的具体信息。
实际上模型的描述是解决问题非常重要的一个环节,整个线程场景可以用一种一致的形式来描述,作为 Java 多线程并发控制框架引擎的输入。也就是将线程运行的模式用 XML 来描述出来,这样只用改动 XML 配置文件就可以更改整个线程运行的模式,不用改动任何的源代码。
两种实现机制
对于 Java 多线程的运行框架来说,我们将采用“外”和“内”的两种模式来实现。
“外” - 主线程轮询
图 2. 静态类图
Thread 是工作线程。ThreadEntry 是 Thread 的包装类,prerequisite 是一个 HashMap,它含有 Thread 的先决线程的状态。如图1中显示的那样,T4 的先决线程是 T2 和 T3,那么 prerequisite 中就包含 T2 和 T3 的状态。TestScenario 中的 threadEntryList 中包含所有的 ThreadEntry。
图 3. 线程执行场景
TestScenario 作为主线程,作为一个“外”在的监控者,不断地轮询 threadEntryList 中所有 ThreadEntry 的状态,当 ThreadEntry 接受到 isReady 的查询后查询自己的 prerequisite,当其中所有的先决线程的状态为“正常结束时”,它便返回 ready,那么 TestScenario 便会调用 ThreadEntry 的 startThread() 方法授权该 ThreadEntry 运行线程,Thread 便通过 run() 方法来真正执行线程。并在正常执行完毕后调用
setPreRequisteState() 方法来更新整个 Scenario,threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有该 Thread 的状态信息为“正常结束”。
图 4. 状态更改的过程
如图 1 中所示的 T4 的先决线程为 T2 和 T3,T2 和 T3 并行执行。如图 4 所示,假设 T2 先执行完毕,它会调用 setPreRequisteState() 方法来更新整个 Scenario, threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有该 T2 的状态信息为“正常结束”。此时,T4 的 prerequisite 中 T2 的状态为“正常结束”,但是 T3 还没有执行完毕,所以其状态为“未完毕”。所以 T4 的 isReady 查询返回为
false,T4 不会执行。只有当 T3 执行完毕后更新状态为“正常结束”后,T4 的状态才为 ready,T4 才会开始运行。
其余的节点也以此类推,它们正常执行完毕的时候会在整个的 scenario 中广播该线程正常结束的信息,由主线程不断地轮询各个 ThreadEntry 的状态来开启各个线程。
这便是采用主控线程轮询状态表的方式来控制 Java 多线程运行框架的实现方式之一。
优点:概念结构清晰明了,实现简单。避免采用 Java 的锁机制,减少产生死锁的几率。当发生异常导致其中某些线程不能正常执行完毕的时候,不会产生挂起的线程。
缺点:采用主线程轮询机制,耗费 CPU 时间。当图中的节点太多的(n>??? 而线程单个线程执行时间比较短的时候 t?? 需要进一步研究)时候会产生线程启动的些微延迟,也就是说实时性能在极端情况下不好,当然这可以另外写一篇文章来专门探讨。
“内” - wait¬ify
相对于“外”-主线程轮询机制来说,“内”采用的是自我控制连锁触发机制。
图 5. 锁机制的静态类图
Thread 中的 lock 为当前 Thread 的 lock,lockList 是一个 HashMap,持有其后继线程的 lock 的引用,getLock 和 setLock 可以对 lockList 中的 Lock 进行操作。其中很重要的一个成员是 waitForCount,这是一个引用计数。表明当前线程正在等待的先决线程的个数,例如图 1 中所示的 T4,在初始的情况下,他等待的先决线程是 T2 和 T3,那么它的 waitForCount 等于 2。
图 6. 锁机制执行顺序图
当整个过程开始运行的时候,我们将所有的线程 start,但是每个线程所持的 lock 都处于 wait 状态,线程都会处于 waiting 的状态。此时,我们将 root thread 所持有的自身的 lock notify,这样 root thread 就会运行起来。当 root 的 run 方法执行完毕以后。它会检查其后续线程的 waitForCount,并将其值减一。然后再次检查 waitForCount,如果 waitForCount 等于 0,表示该后续线程的所有先决线程都已经执行完毕,此时我们 notify
该线程的 lock,该后续线程便可以从 waiting 的状态转换成为 running 的状态。然后这个过程连锁递归的进行下去,整个过程便会执行完毕。
我们还是以 T2,T3,T4 为例,当进行 initThreadLock 过程的时候,我们可以知道 T4 有两个直接先决线程 T2 和 T3,所以 T4 的 waitForCount 等于 2。我们假设 T3 先执行完毕,T2 仍然在 running 的状态,此时他会首先遍历其所有的直接后继线程,并将他们的 waitForCount 减去 1,此时他只有一个直接后继线程 T4,于是 T4 的 waitForCount 减去 1 以后值变为 1,不等于 0,此时不会将 T4 的 lock notify,T4 继续
waiting。当 T2 执行完毕之后,他会执行与 T3 相同的步骤,此时 T4 的 waitForCount 等于 0,T2 便 notify T4 的 lock,于是 T4 从 waiting 状态转换成为 running 状态。其他的节点也是相似的情况。
当然,我们也可以将整个过程的信息放在另外的一个全局对象中,所有的线程都去查找该全局对象来获取各自所需的信息,而不是采取这种分布式存储的方式。
优点:采用 wait¬ify 机制而不采用轮询的机制,不会浪费CPU资源。执行效率较高。而且相对于“外”-主线程轮询的机制来说实时性更好。
缺点:采用 Java 线程 Object 的锁机制,实现起来较为复杂。而且采取一种连锁触发的方式,如果其中某些线程异常,会导致所有其后继线程的挂起而造成整个 scenario 的运行失败。为了防止这种情况的发生,我们还必须建立一套线程监控的机制来确保其正常运行。
延伸
下面的图所要表达的是这样一种递归迭代的概念。例如在图1 中展示的那样,T1 这个节点表示的是一个线程。现在,忘掉线程这样一个概念,将 T1 抽象为一个过程,想象它是一个银河系,深入到 T1 中去,它也是一个许多子过程的集合,这些子过程之间的关系模式就如图 1 所示那样,可以用一个图来表示。
图 7. 嵌套子过程
可以想象一下这是怎样的一个框架,具有无穷扩展性的过程框架,我们只用定义各个过程之间的关系,我们不用关心过程是怎样运行的。事实上,可以在最终的节点上指定一个实际的工作,比如读一个文件,或者submit一个JCL job,或者执行一条sql statement。
其实,按照某种遍历规则,完全可以将这种嵌套递归的结构转化成为一个一层扁平结构的图,而不是原来的分层的网状结构,但是我们不这样做的原因是基于以下的几点考虑:
如果这样做,会导致图节点太多,边太多,令人眼花缭乱。
不这样做更主要的原因是每一个场景,如图 7 中的 T1,T13,是状态聚集的一个单元,具有高复用性和可靠性。
框架是高度抽象的,它实际的执行可以是分布式的,一个单元可以是一个系统,作为和其他系统的分界标志。
实际上,这是一个状态聚集的层次控制框架,我们可以依赖此框架来执行自主运算。我们将在其它的文章中来讨论它的应用。
总结
本文介绍了一种 Java 多线程并发控制的框架,并给出了其两种实现的模型,它们有各自的优缺点,有各自的适用范围。当需要进行 Java 线程的并发控制的时候,可以作为参考。
java 线程管理框架_实现 Java 多线程并发控制框架相关推荐
- java线程概念_《Java基础知识》Java线程的概念
按照规划,从本篇开始我们开启『并发』系列内容的总结,从本篇的线程开始,到线程池,到几种并发集合源码的分析,我们一点点来,希望你也有耐心,因为并发这块知识是你职业生涯始终绕不过的坎,任何一个项目都或多或 ...
- java 线程状态_关于JAVA线程状态
最近在复习java基础知识,在看到java多线程知识的时候偶然搜到一篇csdn上的博客. 这篇博客上弄了一张描述java线程状态转换的图,如下 看到的第一眼直觉上告诉这图我哪里不太对,于是我就去了的相 ...
- java线程提交_从Java线程到线程池
线程模型 线程模型分为两类,用户级线程(ULT)和内核级线程(KLT) 用户级线程(ULT):user level threads,系统内核对ULT无感知,线程的创建和调度都由用户级APP进程管理:即 ...
- java 线程转储_获取Java线程转储的常用方法(推荐)
1. 线程转储简介 线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照. 线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分 ...
- java 线程状态_浅析Java中的线程状态
一.线程的5种状态 众所周知,Java的线程状态有5种,分别对应上图中五种不同颜色,下面对这5种状态及状态间的转化做相应的解释: 1. 初始化状态:新建一个线程对象 2. 可运行状态:其他线程调用了该 ...
- java 线程 内部_从Java中的main()内部在线程实例上运行wait()
我正在使用java.lang.Object中的wait()的定时版本,并观察到它在两种不同的情况下的行为不同. 方案1:在线程中使用run()的默认定义 public static void main ...
- 实现 Java 多线程并发控制框架
2006 年 8 月 14 日 Java 提供了语言级别的线程支持,所以在 Java 中使用多线程相对于 C,C++ 来说更简单便捷,但本文并不是介绍如何在 Java 中使用多线程来来解决诸如 Web ...
- 自定义java线程池_我的Java自定义线程池执行器
自定义java线程池 ThreadPoolExecutor是Java并发api添加的一项功能,可以有效地维护和重用线程,因此我们的程序不必担心创建和销毁线程,也不必关注核心功能. 我创建了一个自定义线 ...
- JAVA线程池管理及分布式HADOOP调度框架搭建
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...
最新文章
- LabVIEW保存、读取配置文件
- 如何结合因果与强化学习?看最新《因果强化学习:动机,概念,挑战与应用》报告,85页ppt...
- OSPF常见错误和排错方法
- 【数学与算法】PCA主成分分析(降维)的通俗理解
- VS2008 Web Application和Web Site的区别_转载
- CMA内存管理子系统
- 上市公司总市值TOP10出炉,你所在县区看的到未来吗?
- MongoDB 去重(distinct)查询后求总数(count)
- 关于group by的用法 原理
- 扫地机器人的特点描写_扫地机器人的特点是什么
- 微信打击违规贷款、仿冒公众号等行为 累积处罚3万多个公众号
- FBI或被允许隐瞒解锁iPhone技术 不向苹果公开
- Unity3D 拆包工具 AssetStudio 编译构建
- R语言读取Excel的神器——openxlsx
- OfficeExcel(5)
- IMDB电影排行爬取分析
- 360漏洞修复网管版小软件不错
- java中线程池的实现原理:七参、四策
- Allan方差读图分析IMU误差指标
- 金蝶云苍穹轻量级开发人员申请试用
热门文章
- (造轮子)C 创建队列和图实现广度优先算法(BFS)和深度优先算法(DFS)(数据结构)
- 云和恩墨大讲堂丨PostgreSQL逻辑复制案例分享
- 2021 ACDU China Tour-上海站暨数据库大咖讲坛(第4期)成功举办!(附视频回放PPT下载)...
- 聚焦openEuler Summit,解锁云原生、开源等领域的实践干货
- 直播丨 SQL大赛冠军怀晓明:深入解析Oracle存储过程中的性能瓶颈
- 2019年9月数据库流行度排行:MySQL 强劲增长完成深 V 反转
- 实践:在运维大数据这事上,Apache Kylin比ELK更擅长?
- 案例分析:你造吗?有个ORA-60死锁的解决方案
- Hive on Spark和Spark sql on Hive,你能分的清楚么
- Java编程中忽略这些细节,Bug肯定找上你