【问题标题】:How to publish StringBuffer safely?如何安全地发布 StringBuffer?
【发布时间】:2016-04-06 09:58:22
【问题描述】:

由于StringBuffer 是线程安全的,因此可以安全地发布。考虑StringBuffersources)的公共构造函数:

public StringBuffer() {
    super(16);
}

super(16) 指定这个:

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

其中值声明为

char[] value;

问题:如何安全地发布StringBuffer

我有以下课程:

public class Holder{
     public final StringBuffer sb = new StringBuffer();
}

它可以被视为安全发布吗? 我认为,它不能。

final 保证我们将看到引用 sb 的新值。但是在AbstractStringBuilder(int capacity) 中写入sb 的内部状态是不同步的。因此,没有happens-before 顺序,这反过来意味着在构造函数中调用sb.append(2); 和写入value 时发生从value 的读取是racy

你能帮忙理解一下吗?也许我错过了什么......

【问题讨论】:

  • 你的意思是sb 是一个实例变量还是应该是静态的?
  • 如果您想要一个不时更改的文本/字符串的线程安全提供程序,请使用AtomicReference<String>。 StringBuffer 用于并发追加,这是一个罕见的用例。
  • @Paolo 不,它完全是一个实例变量。静态初始化发生在初始化需要跨 JVM 锁定的类时,从而确保内存一致性。
  • 我认为您对这里的安全或不安全感到困惑。构造函数将在引用分配之前完成,在这方面有一个happens-before:如果您可以看到引用,那么您也可以看到构造函数中发生的所有事情。唯一不安全的做法是将构造函数中的this 泄漏到其他地方,而StringBuilder 不会发生这种情况。
  • @JoopEggen 问题不完全是关于寻找另一种方式。我只是想了解使用 StringBuffer 是否安全。

标签: java multithreading safe-publication


【解决方案1】:

你能帮忙理解一下吗?也许我错过了什么……

JSR-133 之后的 AFAIR 保证在您的示例中初始化的类的 final 字段在实例化过程中没有竞争条件,并且只有正确的值才会在初始化后公开

更新:通过Brian Goetz

在新的内存模型下,在构造函数中写入最终字段与在另一个线程中对该对象的共享引用的初始加载之间存在类似于发生前的关系。当构造函数完成时,所有对 final 字段(以及可通过这些 final 字段间接访问的变量)的写入都将被“冻结”,并且在冻结后获得对该对象的引用的任何线程都可以保证看到所有冻结的值冰冻的田野。初始化 final 字段的写入不会在与构造函数关联的冻结之后的操作中重新排序。

恕我直言,您的问题(和并发理解)非常好,因为它不是一个明显的语言/平台设计功能,并且仅在 Java SE 5.0 中得到修复

【讨论】:

  • 正确的参考值,是的。但是它对对象的状态引用呢?
  • 是的,您最近的更新说明了一切。谢谢。
  • 因此,如果没有 final 修饰符,它不会被视为安全发布。对吗?
  • 是的。没有final,在当前的 JMM 下是不安全的。
【解决方案2】:

请参阅 StringBuffer 的 javadoc。

字符串缓冲区可以安全地被多个线程使用。这些方法在必要时同步,以便任何特定实例上的所有操作都表现得好像它们以某种串行顺序发生,这与所涉及的每个单独线程进行的方法调用的顺序一致。

这应该是足够的保证。

【讨论】:

    【解决方案3】:

    创建sb 的实例变量是线程安全的,因为它与创建Holder 的实例同时完成。

    它是线程安全的原因是它是一个实例变量,线程 1 无法开始构造 Holder 的实例(并通过关联新的 StringBuffer)然后让第二个线程跳入并开始运行同一个实例的构造函数。

    也就是说,如果您的代码有两个线程都遇到了这些行

    Holder h = new Holder();
    

    您最终会得到两个独立的 Holder 实例,然后是分配给 h 的竞赛,这是一个不同的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      • 2021-05-18
      • 1970-01-01
      • 2020-07-09
      相关资源
      最近更新 更多