来自的注释--在实践中的并发性
任何线程都可以安全地使用不可变对象,而不需要额外的同步,即使不使用同步来发布它们。
我得到的是,它们是线程安全的。
杰里米·曼森博客上的片段-
class String {
// Don't do this, either.
static String lastConstructed;
private final byte[] bytes;
public String(byte[] value) {
bytes = new byte[value.length];
System.arraycopy(value, 0, bytes, 0, value.length);
lastConstructed = this;
}
}由于此引用存储在lastConstructed“中,因此转义构造函数
当然,如果您使lastConstructed易失性(postJDK5+语义),它将工作。
其中一个问题是-
如果lastConstructed是不稳定的,但是引用被不安全地发布到另一个线程,那么字符串就不会是不可变的。对吗?
杰里米的回答是-
它不会是线程安全的,因为它是不可变的,但它将是线程安全的,因为lastConstructed是不稳定的。
我完全理解,因为lastConstructed是不稳定的,所以它是线程安全的,但是我没有得到--它不会线程安全,因为它是不可变的。
为什么?在实践中来自并发性的注释说,不可变对象可以安全地被任何线程使用(即线程安全保证)。如果某些东西是不可变的,那么它就是线程安全的。
请建议一下。
发布于 2016-09-04 08:39:43
一个常见的错误理解是,Java中有对象字段。您只有引用和原语。这意味着
static String lastConstructed;字段lastConstructed是可变的引用。它的可视性不是线程安全的。拥有不可变对象并不会赋予对该对象的引用任何属性。
同样,如果您有一个final字段,这不会使您的对象不可变。
final Date today = new Date();today并不是不可变的,仅仅因为您对它进行了一次引用,final。
更微妙的是在使用volatile时,您必须小心您是在读取还是写入易失性值。即使你这么做
volatile Date now = new Date();
now.setTime(System.currentTimeMillis()); // no thread safe.这不是线程安全的原因有两个。1)对now的访问是读而不是写,其次,在任何情况下,它都发生在写入之前。需要的是后面的写障碍。你看到的似乎是胡说八道的东西。
now = now; // this adds a write barrier.一个相关的神话是,如果您使用线程安全集合,那么您执行的任何一系列操作都是线程安全的。它有点像仙女的灰尘,你只是把它撒在周围,你的许多but消失了,但这并不意味着你真的是线程安全。
简而言之,线程安全就像一个依赖链,如果访问数据的任何方面都不是线程安全的,那么没有一个是线程安全的。
注意:添加一些线程安全结构可以隐藏线程安全问题,但它并没有解决这些问题,这只是意味着JVM中的一些更改,或者操作系统或硬件将来会破坏您的代码。
发布于 2016-09-04 09:25:13
尽管@Peter已经解释了设计线程安全和不可变类的细节和问题,并且基于进一步的讨论,我认为这个问题还没有得到直接的回答。所以,我想再详细阐述一下:
理解“不可变对象可以被任何线程安全使用”这一短语的主要问题是,它本身是不完整的。它所依赖的前提是,任何对象也必须安全发布才能实现线程安全。不可变对象也不例外。因此,完整的短语应该是“不可变的,安全发布的对象可以安全地被任何线程使用”。
String示例中的问题是,它允许对对象的引用从构造函数中转义,从而向其他线程呈现潜在无效的对象状态。例如,如果编译器出于性能原因决定优化构造函数并重新排列操作,则如下所示:
public String(byte[] value) {
bytes = new byte[value.length];
lastConstructed = this;
System.arraycopy(value, 0, bytes, 0, value.length);
}其他一些读取lastConstructed的线程将能够看到未完全构造的字符串。因此,类并不是线程安全的,即使它的实例是不可变的。
在这里,我们谈到了“它不会是线程安全的,因为它是不变的”这个短语的意思。这意味着不变性本身并不能保证线程安全,并通过实例证明了这一点。
使lastConstructed易失性将迫使编译器发出一个内存屏障,以防止优化器以上述方式重新安排操作。它将确保数组复制总是在赋值lastConstructed = this之前进行。因此,另一个线程,即读取lastConstructed的线程,将永远不会看到构造不足的字符串。它还将确保其他线程始终读取lastConstructed的实际值。这就是为什么在这种特殊情况下,“它将是线程安全的,因为lastConstructed是不稳定的”。
https://stackoverflow.com/questions/39315015
复制相似问题