Eclips运行时概述4
1.1.1 调度规则
作业调度规则可以用来控制作业的运行何时与其它作业有关。特别是,调度规则使您可以防止多个作业在并发性可能导致不一致结果的情况下同时运行。它们还使您可以保证一系列作业的执行顺序。通过一个示例很好地说明了调度规则的作用。让我们从定义用来同时将灯打开和关闭的两个作业开始:
public class LightSwitch {
private boolean isOn = false;
public boolean isOn() {
return isOn;
}
public void on() {
new LightOn().schedule();
}
public void off() {
new LightOff().schedule();
}
class LightOn extends Job {
public LightOn() {
super("Turning on the light");
}
public IStatus run(IProgressMonitor monitor) {
System.out.println("Turning the light on");
isOn = true;
return Status.OK_STATUS;
}
}
class LightOff extends Job {
public LightOff() {
super("Turning off the light");
}
public IStatus run(IProgressMonitor monitor) {
System.out.println("Turning the light off");
isOn = false;
return Status.OK_STATUS;
}
}
}
现在,创建一个简单的程序,它会创建灯开关,然后将灯打开并再次将灯关闭:
LightSwitch light = new LightSwitch();
light.on();
light.off();
System.out.println("The light is on? " + switch.isOn());
如果运行这个小程序次数足够多,最终将获得以下输出:
Turning the light off
Turning the light on
The light is on? true
结果将如何?我们已经让灯先打开然后再关闭,所以它的最终状态应该是关闭的!问题是没有采取任何措施防止LightOff作业与LightOn作业同时运行。因此,即使首先调度了“打开”作业,但是只要它们同时执行,就无法预测这两个同时执行的作业的准确执行顺序。如果LightOff作业在LightOn作业之前结束运行,则会获得此无效结果。我们需要采用某种方法来防止这两个作业同时运行,这就是调度规则要完成的任务。
可以通过创建充当互斥对象(也称为二进制信号灯)的简单调度规则来修正此示例:
class Mutex implements ISchedulingRule {
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
}
public boolean contains(ISchedulingRule rule) {
return rule == this;
}
}
然后,将此规则添加到前一示例中的两个灯开关作业中:
public class LightSwitch {
final MutextRule rule = new MutexRule();
...
class LightOn extends Job {
public LightOn() {
super("Turning on the light");
setRule(rule);
}
...
}
class LightOff extends Job {
public LightOff() {
super("Turning off the light");
setRule(rule);
}
...
}
}
现在,当调度这两个灯开关作业时,作业基础结构将调用isConflicting方法来比较这两个作业的调度规则。它将注意到这两个作业具有冲突的调度规则,并将确保它们按正确的顺序运行。它还将确保这两个作业从不同时运行。现在,即使您无数次运行该示例程序,始终都将获得相同的结果:
Turning the light on
Turning the light off
The light is on? false
还可以将规则用作常规锁定机制独立于作业。以下示例将获取 try/finally 块中的规则,在调用beginRule与调用endRule之间的这段时间内防止其它线程和作业使用该规则运行。
IJobManager manager = Platform.getJobManager();
try {
manager.beginRule(rule, monitor);
... do some work ...
} finally {
manager.endRule(rule);
}
当使用这种编码模式来获取和释放调度规则时,应该特别小心。如果未能结束已经对其调用了beginRule的规则,将永久锁定该规则。
1.1.2 建立您自己的规则
尽管作业 API 定义了调度规则的合同,但是它实际上并未提供任何调度规则实现。实质上,一般的基础结构没有办法知道哪些作业集可以同时运行。缺省情况下,作业没有调度规则,执行已调度作业的速度与可以创建线程来运行它的速度一样快。
当作业确实具有调度规则时,isConflicting方法用来确定规则是否与当前正在运行的任何作业的规则发生冲突。这样,isConflicting的实现可以准确定义何时执行作业是安全的。在我们的灯开关示例中,isConflicting实现只是将标识比较与所提供的规则配合使用。如果另一个作业具有完全相同的规则,则它们将不会同时运行。当编写您自己的调度规则时,一定要仔细阅读并遵循isConflicting的 API 合同。
如果作业具有几个不相关的约束,则可以使用 MultiRule 将多个调度规则组合起来。例如,如果作业需要打开灯开关,并且还要将信息写入网络套接字,则它可以使用工厂方法MultiRule.combine将用于灯开关的规则与用于套接字的写访问权的规则组合成一条规则。
1.1.3 规则层次结构
我们已讨论有关 ISchedulingRule 的isConflicting方法,但是还远未提到contains方法。此方法用于许多客户机将不需要的调度规则的相当特殊的应用程序。调度规则可以按逻辑组成层次结构,用来控制对自然分层的资源的访问权。用来说明此概念的最简单示例是基于树状结构的文件系统。如果应用程序想获得对目录的互斥锁定,则通常意味着它还想要对该目录中的文件和子目录的互斥访问权。contains方法用来指定锁定之间的分层关系。如果不需要创建锁定的层次结构,则可以实现contains方法以便只调用isConflicting。
下面是用于控制对java.io.File句柄的写访问权的分层锁定的示例。
public class FileLock implements ISchedulingRule {
private String path;
public FileLock(java.io.File file) {
this.path = file.getAbsolutePath();
}
public boolean contains(ISchedulingRule rule) {
if (this == rule)
return true;
if (rule instanceof FileLock)
return path.startsWith(((FileLock) rule).path);
if (rule instanceof MultiRule) {
MultiRule multi = (MultiRule) rule;
ISchedulingRule[] children = multi.getChildren();
for (int i = 0; i < children.length; i++)
if (!contains(children[i]))
return false;
return true;
}
return false;
}
public boolean isConflicting(ISchedulingRule rule) {
if (!(rule instanceof FileLock))
return false;
String otherPath = ((FileLock)rule).path;
return path.startsWith(otherPath) || otherPath.startsWith(path);
}
}
如果线程在已经拥有一个规则的情况下试图获取第二个规则,则可以使用contains方法。为了避免可能发生的死锁,任何给定的线程在任何给定的时间都只能拥有一个调度规则。如果线程在已经拥有一个规则的情况下调用beginRule,则无论是通过前一次对beginRule的调用还是通过使用调度规则执行作业,都会使用contains方法查明这两个规则是否等效。如果已拥有的规则的contains方法返回true,则调用beginRule将成功。如果contains方法返回false,则将发生错误。
为了更具体地说明此情况,假定线程在“c:\temp”目录中拥有示例FileLock规则。虽然它拥有此规则时,但是只允许它修改该目录子树中的文件。如果它试图修改不在“c:\temp”下面的其它目录中的文件,则它将失败。因此,调度规则是一个具体规范,用来说明允许或不允许作业或线程执行的操作。违反该规范将导致运行时异常。在并发性文献资料中,此技术称为两阶段锁定。在两阶段锁定方案中,进程提前很长时间指定对于特定任务它将需要的所有锁定,然后不允许在操作期间获得更多锁定。两阶段锁定消除了挂起并等待情况,挂起并等待情况是循环等待死锁的先决条件。因此,只将调度规则用作锁定原语的系统不可能进入死锁。
1.1.4 锁定
系统中的多个作业有可能需要访问和处理同一个对象。ILock定义用于授予对共享对象的互斥访问的协议。当作业需要访问共享对象时,它会获取对该对象的锁定。当它处理完该对象时,它就会释放该锁定。
锁定通常是在插件创建或者第一次访问共享对象时创建的。即,引用共享对象的代码也会引用对共享对象的锁定。我们将从创建 myLock 锁定开始,将使用该锁定来控制对 myObject 的访问权:
...
myObject = initializeImportantObject();
IJobManager jobMan = Platform.getJobManager();
myLock = jobMan.newLock();
...
平台提供了 ILock 的健壮实现。作业管理器提供了此锁定的实例供客户机使用。这些锁定都相互了解,并且可以避免循环死锁。(我们将立即对此进行更详细的说明。)
每当作业中的代码需要访问 myObject 时,它就必须首先获得对该对象的锁定。以下片段说明使用锁定的常见习惯用语:
...
// I need to manipulate myObject, so I get its lock first.
try {
myLock.acquire();
updateState(myObject); // manipulate the object
} finally {
lock.release();
}
...
在可以对调用作业授予对锁定的互斥访问之前,将不会返回 acquire() 方法。换句话说,如果其它某个作业已经获得锁定,则此代码将被阻塞,直到锁定可用为止。注意,获得锁定并处理 myObject 的代码包括在try块中,因此,如果在使用对象时发生任何异常,则可以释放该锁定。
看起来很简单,对吗?幸好,锁定使用起来相当简单。它们还是可重入的,这意味着不必担心作业多次获得同一锁定。每个锁定都会对获得和释放特定线程的次数进行计数,并且仅当释放次数等于获得次数时才会从作业中释放锁定。
死锁
我们前面提到了作业管理器提供的锁定之间是相互了解的,并且可以避免循环死锁。要了解死锁是如何发生的,让我们查看一个简单的方案。假定“作业 A”获得“锁定 A”,随后尝试获得“锁定 B”。同时,“锁定 B”被“作业 B”挂起,而“作业 B”现在已被阻塞,它正在等待“锁定 A”。这种死锁表示在作业之间使用锁定时存在底层设计问题。虽然这种简单情况很容易避免,但是,随着设计中使用的作业数和锁定数逐渐增加,意外产生死锁的机会也将增大。
幸好,平台将帮助您发现死锁。当作业管理器检测到死锁情况时,它就会将诊断信息显示在日志中,并且会描述死锁情况。然后,它通过以下方法来解除死锁:将对被阻塞的作业拥有的锁定的访问权临时授予给正在等待这些锁定的其它作业。仔细测试涉及到多个锁定的任何实现并修正平台报告的任何死锁情况是很重要的。
Eclips运行时概述4相关推荐
- Eclipse运行时概述1(运行时插件模型,首选项)
1 运行时概述 Eclipse 运行时定义插件(org.eclipse.osgi和org.eclipse.core.runtime),其它所有插件都依赖于它们.运行时负责定义插件的结构以及它 ...
- JavaScript 对引擎、运行时、调用堆栈的概述理解
JavaScript 对引擎.运行时.调用堆栈的概述理解 随着JavaScript越来越流行,越来越多的团队广泛的把JavaScript应用到前端.后台.hybrid 应用.嵌入式等等领域. 这篇文 ...
- JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈
运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...
- java 异常继承体系_1、异常概述和继承体系 2、原因及处理方式 3、运行时异常 4、重写异常处理 5、Throwable类常见方法 6、自定义异常...
01异常的概述 * A: 异常的概述 * a:什么是异常 * Java代码在运行时期发生的问题就是异常. * b:异常类 * 在Java中,把异常信息封装成了一个类. * 当出现了问题时,就会创建异常 ...
- jconsole 里的线程编号一直在增加_第三章_运行时数据区概述及线程
运行时数据区概述及线程 概述 本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段 当我们通过前面的:类的加载-> 验证 -> 准备 -> 解析 -> 初始化 ...
- 第 3 章 运行时数据区概述及线程
第 3 章 运行时数据区概述及线程 1.前言 本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段 当我们通过前面的:类的加载 --> 验证 --> 准备 --> ...
- JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!
JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 摘要: 理解JS执行原理. 原文:JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 作者:前端小智 Fundebug经 ...
- 【JVM】运行时数据区概述(程序计数器、虚拟机栈、本地方法栈)
前言 本节主要讲的是运行时数据区,也就是下图这部分,它是在类加载完成后的阶段 当我们通过前面的:类的加载-> 验证 -> 准备 -> 解析 -> 初始化 这几个阶段完成后,就会 ...
- Java8 JVM运行时数据区概述 (极其详细长文)
文章目录 运行时数据区概述 JVM中的线程说明 PC寄存器(PC Register) PC寄存器介绍 使用举例 问题:使用PC寄存器存储字节码指令地址有什么用?为什么使用PC寄存器存储? 问题:为什么 ...
最新文章
- 本地jar文件中搜索class
- 微信黑科技-推荐系统,一文带你看懂为什么微信推荐这么快?
- Equipment download和自动生成的IBASE DB 信息
- 在SD/MMC卡中可读写的FAT文件系统
- 理解矩阵,矩阵背后的现实意义 [转]
- Python 精要参考(第二版) 第八章 模块和包
- CentOS 6.9之LVM创建,扩容
- Vue:vue过渡、动画特效以及借助animate.css实现动画效果
- Linux Ubuntu/Centos7 定时备份mysql数据库
- 简易java电子词典_使用Android简单实现有道电子词典
- access更新查询非汉族加分_计算机二级Access:如何创建更新查询
- Mac部分按键失灵问题解决
- webpack中vender的抽离
- HBuilder详细安装教程
- html实现在线聊天,利用HTML5实现电脑端微信聊天窗口界面
- 网站SEO优化多少钱 网站seo优化费用多少
- XStream学习手册
- 公众号开发——自动回复功能
- 不是为了赢,只是不想输
- html5如何制作边框,html5 边框怎么设置
热门文章
- Port 80 in use by “Unable to open process“ with PID 4!
- PDF怎样转换成JPG图片 PDF转换为JPG图片教程
- 让心灵插上翅膀自由飞翔——之北戴河
- 福昕高级PDF编辑器Foxit PhantomPDF Business v9.3.0.10826企业版+破解补丁
- 关于SQL Server numeric数据类型介绍
- lnln(10 3.5 2)的c语言,ln10(ln对数表)
- 创建程序编写demo.py文件
- Matlab——行星轨道问题
- 2019-2022年中国定期存款基准利率走势(附三个月定期、半年定期、一年定期、二年定期及三年定期存款基准利率)[图]
- DICTATOR第一周二分查找作业