由于StringBuffer
是线程安全的,因此可以安全地发布它。考虑StringBuffer
(sources)的公共构造函数:
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
的新值。但是在sb
内写入AbstractStringBuilder(int capacity)
的内部状态是不同步的。因此,没有happens-before
顺序,这反过来意味着在调用value
并在构造函数中写入sb.append(2);
时发生的value
读取是 racy 。
你能帮忙理解这个吗?也许我错过了什么......
答案 0 :(得分:4)
你能帮忙理解这个吗?也许我错过了什么......
在JSR-133
之后的AFAIR保证在实例化过程中初始化的类final
字段在实例化过程中没有竞争条件,并且只有正确的值将在init之后公开
更新:通过Brian Goetz
在新的内存模型下,有一些类似于构造函数中最终字段的写入与另一个线程中该对象的共享引用的初始加载之间的先发生关系。当构造函数完成时,对最终字段的所有写入(以及通过这些最终字段间接可到达的变量)都会被冻结,#34;并且在冻结后获得对该对象的引用的任何线程都可以保证看到所有冻结字段的冻结值。在与构造函数关联的冻结之后的操作中,不会对初始化最终字段的写入进行重新排序。
恕我直言你的问题(和并发理解)是非常好的,因为它不是一个明显的语言/平台设计功能,它只在Java SE 5.0中修复
答案 1 :(得分:2)
请参阅StringBuffer的javadoc。
字符串缓冲区可供多个线程使用。这些方法在必要时进行同步,以便任何特定实例上的所有操作都表现得好像它们以某种顺序发生,这与所涉及的每个单独线程所进行的方法调用的顺序一致。
这应该是足够的保证。
答案 2 :(得分:2)
创建sb
的实例变量是线程安全的,因为它与创建Holder
的实例同时完成。
它是线程安全的原因是它是一个实例变量,并且线程1无法开始构建Holder
的实例(并通过关联一个新的StringBuffer
)然后一秒钟线程跳入并开始运行相同实例的构造函数。
那就是如果你的代码有两个线程同时出现
Holder h = new Holder();
你最终会有两个独立的Holder实例,然后将一个竞赛分配给h
,这是一个不同的问题。