如何安全发布StringBuffer?

How to publish StringBuffer safely?

StringBuffer is thread safe it can safely be published. Consider the public constructor of StringBuffer ( sources ):

public StringBuffer() {
    super(16);
}

其中 super(16) 指定这个:

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

其中值声明为

char[] value;

问题:如何安全地发布StringBuffer

我得到了以下 class:

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

能算安全发布吗? 我觉得不行

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

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

Can you help to understand this? Maybe I missed something...

AFAIR 在 JSR-133 之后保证 class 的 final 字段在实例化过程中没有竞争条件,只有正确的值才会在 init

之后暴露

更新: 通过 Brian Goetz

Under the new memory model, there is something similar to a happens-before relationship between the write of a final field in a constructor and the initial load of a shared reference to that object in another thread. When the constructor completes, all of the writes to final fields (and to variables reachable indirectly through those final fields) become "frozen," and any thread that obtains a reference to that object after the freeze is guaranteed to see the frozen values for all frozen fields. Writes that initialize final fields will not be reordered with operations following the freeze associated with the constructor.

恕我直言,你的问题(和并发性理解)非常好,因为它不是一个明显的语言/平台设计特性,它只在 Java SE 5.0

中得到修复

请参阅 StringBuffer 的 javadoc。

String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.

这应该是足够的保证。

创建 sb 的实例变量是线程安全的,因为它是在创建 Holder.

的实例的同时完成的

它是线程安全的原因是它是一个实例变量,线程 1 无法开始构造 Holder 的实例(并通过关联创建一个新的 StringBuffer)然后用于第二个线程跳入并通过 相同实例 .

的构造函数启动 运行

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

Holder h = new Holder();

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