首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不可变对象与线程安全初始化安全

不可变对象与线程安全初始化安全
EN

Stack Overflow用户
提问于 2016-09-04 08:32:39
回答 2查看 411关注 0票数 1

来自的注释--在实践中的并发性

任何线程都可以安全地使用不可变对象,而不需要额外的同步,即使不使用同步来发布它们。

我得到的是,它们是线程安全的

杰里米·曼森博客上的片段-

代码语言:javascript
复制
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是不稳定的,所以它是线程安全的,但是我没有得到--它不会线程安全,因为它是不可变的

为什么?在实践中来自并发性的注释说,不可变对象可以安全地被任何线程使用(即线程安全保证)。如果某些东西是不可变的,那么它就是线程安全的。

请建议一下。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-04 08:39:43

一个常见的错误理解是,Java中有对象字段。您只有引用和原语。这意味着

代码语言:javascript
复制
static String lastConstructed;

字段lastConstructed是可变的引用。它的可视性不是线程安全的。拥有不可变对象并不会赋予对该对象的引用任何属性。

同样,如果您有一个final字段,这不会使您的对象不可变。

代码语言:javascript
复制
final Date today = new Date();

today并不是不可变的,仅仅因为您对它进行了一次引用,final

更微妙的是在使用volatile时,您必须小心您是在读取还是写入易失性值。即使你这么做

代码语言:javascript
复制
volatile Date now = new Date();

now.setTime(System.currentTimeMillis()); // no thread safe.

这不是线程安全的原因有两个。1)对now的访问是读而不是写,其次,在任何情况下,它都发生在写入之前。需要的是后面的写障碍。你看到的似乎是胡说八道的东西。

代码语言:javascript
复制
now = now; // this adds a write barrier.

一个相关的神话是,如果您使用线程安全集合,那么您执行的任何一系列操作都是线程安全的。它有点像仙女的灰尘,你只是把它撒在周围,你的许多but消失了,但这并不意味着你真的是线程安全。

简而言之,线程安全就像一个依赖链,如果访问数据的任何方面都不是线程安全的,那么没有一个是线程安全的。

注意:添加一些线程安全结构可以隐藏线程安全问题,但它并没有解决这些问题,这只是意味着JVM中的一些更改,或者操作系统或硬件将来会破坏您的代码。

票数 1
EN

Stack Overflow用户

发布于 2016-09-04 09:25:13

尽管@Peter已经解释了设计线程安全和不可变类的细节和问题,并且基于进一步的讨论,我认为这个问题还没有得到直接的回答。所以,我想再详细阐述一下:

理解“不可变对象可以被任何线程安全使用”这一短语的主要问题是,它本身是不完整的。它所依赖的前提是,任何对象也必须安全发布才能实现线程安全。不可变对象也不例外。因此,完整的短语应该是“不可变的,安全发布的对象可以安全地被任何线程使用”。

String示例中的问题是,它允许对对象的引用从构造函数中转义,从而向其他线程呈现潜在无效的对象状态。例如,如果编译器出于性能原因决定优化构造函数并重新排列操作,则如下所示:

代码语言:javascript
复制
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是不稳定的”。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39315015

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档