返回参考时我是否获得了锁定

时间:2017-07-01 14:39:39

标签: java multithreading

我正在浏览cmis API的源代码。其中一种方法定义如下。

public OperationContext getDefaultContext() {
    lock.readLock().lock();
    try {
        return defaultContext;
    } finally {
        lock.readLock().unlock();
    }
}

我想知道,是否有必要在返回引用时获取锁定?如果需要,我为什么要获得锁?

2 个答案:

答案 0 :(得分:3)

锁定的目的之一是建立所谓的先发生关系。允许计算机对操作进行重新排序,这样您可能直观地认为“A,然后B”实际上可能看起来像“A,然后B”到一个线程,但“B,然后A”到另一个线程。同步所做的一件事就是基本上告诉计算机,“你必须做A,然后B。”

这可能导致非常令人困惑的案件。例如,您可以看到所谓的“部分写入”,其中线程只能看到某些操作的一部分。想象一下,如果您在defaultContext上设置两个变量:defaultContext.a = "a"; defaultContext.b = "b";。包括return语句,您有三个操作:读取defaultContext(返回它),写 a b 。线程可以看到它们被命名为defaultContext.b = "b" ; return defaultContext; defaultContext.a = "a";。其中的第三个可能会晚得多,甚至永远不会!换句话说,该线程将看到对defaultContext.b的写入,但不会写入对defaultContext.a的写入:部分写入。

在这种情况下,A和B是对变量defaultLock的写入和读取。如果多个线程正在访问defaultLock(即使某个线程正在读取它,而其他线程只读它),那么您需要进行同步,以便每个线程都具有完全最新的defaultContext。你告诉计算机:“那些写入A和B,他们确实必须在返回之前来。”

请注意,要使其正常工作,您必须仔细设置,因为对defaultContext的任何后续写入都不会同步。您必须有一个线程执行对局部变量的所有写入(即创建并初始化默认上下文),然后将该局部变量写入defaultContext,同时保持lock的写锁定,之后没有其他线程可以修改defaultContext。在这种情况下,有一种更简单的方法:只需使defaultContext成为volatile字段,其写入和读取具有类似的先发生关系。我不能推测为什么代码的作者没有这样做。

假设您正在查看OpenCMIS的SessionImpl代码,这似乎正是正在发生的事情。对defaultContext的所有读取都是通过您粘贴的getDefaultContext方法完成的,所有写操作都是通过使用writeLock的setDefaultContext完成的。​​

答案 1 :(得分:2)

这根本没有意义 - 至少在单独看这个方法的时候。

此代码可能序列化获取该上下文对象的行为。但这样做毫无意义。

除非当然有相应的制定者以类似的方式实施。另一个答案很好地概述了这样的构造如何为写入或读取基础字段的潜在调用创建订单。

但事情是:没有进一步的解释,隐含行为是非常隐蔽的。就个人而言,我更喜欢更明确的解决方案。例如,不使用锁定方法,而是使getter和setter同步。或者至少在两种方法中都添加注释来解释使用该锁定背后的想法。