gpt4 book ai didi

java - 建立非最终领域之前发生的最便宜方法

转载 作者:搜寻专家 更新时间:2023-11-01 02:09:02 27 4
gpt4 key购买 nike

许多问题/答案已经表明,如果类对象具有final字段,并且在构造期间没有任何其他线程公开该对象,则可以保证一旦构造函数完成,所有线程都将看到写入该字段的值。他们还指出,将对一个可变对象的引用存储到final字段中,该对象从未被外部线程访问过,这将确保在存储之前对该对象进行的所有更改在所有访问该对象的线程上都可见通过现场。不幸的是,两种保证都不适用于非final字段的写入。

但是,我没有看到一个问题是这样的:如果类的语义使得字段不能为final,但是希望确保字段和由此标识的对象的“发布”,那是什么?有效的方式吗?例如,考虑

class ShareableDataHolder<T>
{
Object data; // Always identifies either a T or a SharedDataHolder<T>
}
private class SharedDataHolder<T> extends ShareableDataHolder<T>
{
Object data; // Always identifies either a T or a lower-numbered SharedDataHolder<T>
final long seq; // Immutable; necessarily unique
}

目的是 data最初将直接标识数据对象,但可以随时合法地更改它以标识直接或间接封装等效数据对象的 SharedDataHolder<T>。如果对 data的任何读取都可以任意返回曾经写入 data的任何值,但是假定所有代码都被编写为可以正常工作(尽管不一定达到最佳效率),但是如果它读取 null,则可能会失败。

声明 volatile Object data在语义上是正确的,但可能会在以后每次访问该字段时带来额外的费用。最初设置该字段后输入虚拟锁将起作用,但是会很慢。有一个虚拟的 final字段,该对象设置为标识自己的字段似乎可以正常工作。尽管从技术上讲,我认为这可能要求对另一个字段的所有访问都必须通过另一个字段来完成,但我看不到任何实际的情况都重要。无论如何,拥有一个虚假字段只是为了通过它的存在提供适当的同步,这似乎是浪费的。

有什么干净的方法可以通知编译器,构造函数中对 data的特定写入应与该字段的任何读取之间具有事前发生关系,该关系在构造函数返回之后发生(例如,如果该字段为 final,则为这种情况) ),而不必支付与 volatile,锁等相关的费用?或者,如果线程要读取 data并将其查找为空,那么它是否可以某种方式重复读取操作,以便对 data的写入建立“之后发生” [认识到这样的请求可能很慢,但是不需要经常发生]?

PS-如果事前发生关系是不可传递的,那么在以下情况下是否存在正确的事前发生关系?
  • 线程1在某个对象dat中写入非最终字段Fred,并将对它的引用存储到最终字段George中。
  • 线程2将引用从George复制到非最终字段Larry中。
  • 线程3读取Larry.dat

  • 据我所知,在Fred字段 dat的写入与 George的读取之间存在事前发生的关系。在Fred的 dat的写入与 Larry的读取(返回对Fred的引用,该引用从 final引用复制到 Fred的返回)之间是否存在事前关系?如果不是,是否有任何“安全”方式将 final字段中包含的引用复制到可通过其他线程访问的非最终字段?

    PPS-如果在主构造函数完成之前从未在其创建线程之外访问对象及其组成部分,并且主构造函数的最后一步是在主对象中存储对自身的 final引用,是否有任何“合理”的实现/ scenario,另一个线程可以看到部分构造的对象,是否有任何东西实际上使用了 final引用?

    最佳答案

    简短答案

    没有。

    更长的答案

    JLS 17.4.5列出了所有在事前建立关系的方法,而不是final字段语义的特殊情况:


  • 在监视器上进行的每个后续锁定之前,都会发生监视器上的解锁。
  • 在随后每次对该字段进行读取之前,都会对volatile字段(第8.3.1.4节)进行写操作。
  • 在启动线程中的任何操作之前,都会在线程上进行对start()的调用。
  • 线程中的所有操作发生-在任何其他线程从该线程上的join()成功返回之前。
  • 任何对象的默认初始化发生在程序的其他任何动作(默认写操作除外)之前。


  • (原始文件将它们列为要点;为方便起见,我将其更改为数字。)

    现在,您已经排除了锁定(#1)和 Volatile 字段(#2)。规则#3和#4与线程的生命周期相关,您在问题中没有提及,并且听起来并不适用。规则5不提供任何非 null值,因此也不适用。

    因此,在五种可能的事前建立方法中,除了 final字段语义外,其中三种不适用,另外两种已经明确排除。

    * 17.4.5中列出的规则实际上是17.4.4中定义的同步顺序规则的结果,但是这些规则与17.4.5中提到的规则直接相关。我提到这是因为17.4.5的列表可以解释为说明性的,因此并不详尽,但是17.4.4的列表不具说明性和详尽性,如果您不想这样做,可以直接从中进行相同的分析。依靠17.4.5提供的中间分析。

    关于java - 建立非最终领域之前发生的最便宜方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22565155/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com