为什么不锁定基于值的类

时间:2015-12-02 17:29:01

标签: java locking java-8

The docs说你不应该锁定基于值的Java类的实例,例如Optional,因为代码

  

如果试图将两个引用区分为基于值的类的相等值,则可能会产生不可预测的结果......间接通过同步诉求...

Why should Java's value-based classes not be serialized?断言

  

由于未来的JVM实现可能不使用基于值的类的对象头和引用指针,因此一些限制很明显。 (例如,没有锁定JVM必须不支持的标识。锁定的引用可以被删除并在以后被另一个替换,这使得释放锁定毫无意义并将导致死锁)。

即。禁令是面向未来的。但是那个断言没有提及。

如果面向未来的的基础,我想为它提供参考。如果没有,我想了解基础是什么,因为基于价值的对象是Object

修改

BTW,我理解不要锁定Integers和其他原始包装类的原因;他们可能会被缓存。但我找不到任何文档说基于价值的类同样如此,而整数,等等。他们基于价值观are not value-based classes。 I.E. JavaDocs of Optional&等。明确说出

  

这是一个基于价值的课程

Integer,& etc。

也是如此

3 个答案:

答案 0 :(得分:8)

以下是Nicolai Parlog关于基于价值的课程的博客帖子:

  

在Java 8中,值类型前面是基于值的类。它们在未来的精确关系尚不清楚,但它可能类似于盒装和未装箱的原语(例如Integer和int)。此外,编译器可能可以自由地在两者之间切换以提高性能。正确地来回切换,即删除并稍后重新创建引用,也禁止将基于身份的机制应用于基于值的类。

尼古拉所说的是:

  • 将来,编译器可以以不保留对象标识的方式执行在值和基于值的类之间透明转换的事情。

  • 某些事情("基于身份的机制")取决于对象身份。示例包括引用的==语义,标识哈希码,基元锁定和对象序列化。

  • 对于这些事情,透明翻译有可能不透明。

在原始锁定的情况下,关注的是可能发生类似以下序列的事情。

  1. 创建基于值的类的实例。
  2. 实例将在幕后转换为值。
  3. 然后转换回来的值,提供不同的对象。
  4. 如果两个线程然后使用"实例"作为原始锁,他们可能不知道事实上实际上有两个对象(现在)。如果他们然后尝试synchronize,他们会(可能)锁定不同的对象。这意味着无论锁定意图保护的状态如何,都不会互相排斥。

    如果你没有锁定基于价值的课程,你将不必担心这个潜在的危险...... 将来

    但请注意,Nicolai的博客文章是关于Java 10或更高版本可能发生的事情的一个人的猜测。

      

    BTW,我理解不要锁定Integers和其他原始包装类的原因;他们可能会被缓存。

    缓存不是问题本身,而是一种引发问题的机制。真正的问题是难以推断锁对象的对象身份,因此锁定机制是否

    使用原始包装器,装箱和拆箱的语义会增加对象身份的不确定性。接下来,提出的值类型< - >物体转换将是这种不确定性的另一个来源。

    以上博客基于"State of the Values" April 2014. John Rose, Brian Goetz, and Guy Steele,其中讨论了为未来版本的Java添加值类型。本说明是一种立场声明,而不是完整规范(并已采纳)的提案。然而,这个说明确实给了我们这个提示:

      

    "上述许多限制对应于对所谓的基于价值的类的限制。 事实上,每种价值类型的盒装形式似乎都是基于价值的类。"

    可以理解为暗示值类型与现有的基于值的类之间存在关系。 (特别是如果您在value-based classes的Java 8描述的行之间阅读。)

    更新 - 2019/05/18

    值类型没有进入Java 12,它们(尚未)列在Java 13的列表中。

    但是,已经可以来演示与博客文章讨论的问题相关的问题:

        public class BrokenSync {
            private final Integer lock = 1;
    
            public void someMethod() {
                synchronized (lock) {
                    // do something
                }
            }
        }
    

    问题是BrokenSync的每个实例都会通过自动装箱Integer创建1实例。但是JLS说自动装箱产生的Integer个对象不一定是不同的对象。因此,您最终可以使用相同的BrokenSync对象作为锁定来Integer的所有实例。

答案 1 :(得分:0)

锁与对象关联。如果共享一个对象,则可以共享它的锁。可以共享不可变值类。理论上,对具有特定语义值的值对象的所有引用都可以引用一个共享对象。值对象的创建代码通常用于重用值对象。例如,通过缓存以前创建的值。因此,一般情况下,当您引用值对象时,即使值对象也在其他地方使用,您的代码也应该正常工作。所以不要用它来锁。

答案 2 :(得分:0)

所有需要的信息都在您引用标题为“基于价值的课程”的页面上,虽然它没有尽可能清晰地编写,但它没有使用一些可以澄清这些问题的神奇短语。

我将用来描述这种情况的神奇短语是基于价值的类可以具有实现定义的行为。该页面所说的是这些类"不使用"引用相等==。它并没有说实现可能不会定义这样的运算符,但它确实说,实际上,"我们在这一点上保持沉默"。使用的一个短语是"不做任何承诺",这有点清楚。

例如,一种实现方式可能会出于方便,可能会使基于值的类V的行为与大多数其他对象一样,并且不会压制V不使用的成员。另一个可能使用与原始包装类相同的内部机制,以不同方式实现基于值的类。 (如果您正在构建VM,则不必在Java中实现每个类。)只要VM满足此页面中指定的所有要求(或者如果您愿意,合同),它就是& #39; s履行了对用户的义务。

现在假设您编写了锁定此类对象的代码。锁定没有被写入以应对这种情况,因此它将执行某些事情,但它不需要在VM到VM之间保持一致。

要明确您的问题,面向未来的只是实现定义行为的特例。今天的写作方式可能不是明天写的,即使两者都是合法的。