锁定

系统中的多个作业有可能需要访问和处理同一个对象。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”。这种死锁表示在作业之间使用锁定时存在底层设计问题。虽然这种简单情况很容易避免,但是,随着设计中使用的作业数和锁定数逐渐增加,意外产生死锁的机会也将增大。

幸好,平台将帮助您发现死锁。当作业管理器检测到死锁情况时,它就会将诊断信息显示在日志中,并且会描述死锁情况。然后,它通过以下方法来解除死锁:将对被阻塞的作业拥有的锁定的访问权临时授予给正在等待这些锁定的其它作业。仔细测试涉及到多个锁定的任何实现并修正平台报告的任何死锁情况是很重要的。