实例初始化是否安全于类线程

时间:2018-12-18 09:06:29

标签: java multithreading

我一直试图理解多线程的概念,并对以下代码感到困惑:

class MyClass{ 

    private StringBuilder content = new StringBuilder();

    public void setContent(){  
        content.append("Some String");  
        content.append("more String");  
    }

    public String getContent(){  
        return content.toString();  
    }  
}

我的理解是,仅通过同步其setter和getter方法,不能使MyClass成为线程安全的。因为在创建MyClass对象时,内容引用可能具有不正确的对象初始化。为了进行适当的初始化,内容应为最终内容。 谁能帮我澄清一下?

3 个答案:

答案 0 :(得分:1)

  

我的理解是,MyClass不能仅通过同步其setter和getter方法来确保线程安全。

那是不正确的。

提供了MyClass实例的引用安全发布到使用该实例的所有线程,那么synchronized的getter和setter将看到该对象的正确初始状态

如果您声明content(以及任何其他字段)为final,则可以免除安全发布的要求。但是,由于这不是一个不变的类,因此仍需要使getter和setter同步。


final字段的特殊语义(如JLS 17.5中所述)允许真正不可变的对象是线程安全的,而没有任何同步开销。但是这些语义不能直接应用于您的示例中,因为“设置者”正在使对象发生变异。


偶然地,如果content的类型为StringBuffer而不是StringBuilder,并且变量为final。如果没有synchronized,结果将是“大部分”线程安全的。这是因为StringBuilder对于这些操作是线程安全的。唯一的问题是您的“设置者”两次致电append。没有synchronized,getter可能会看到缓冲区的中间状态。

答案 1 :(得分:1)

不可移植性并不总是线程安全性的答案。首先让我们检查现有代码中潜在的线程安全问题。潜在的问题是当您有两个线程(A和B)同时访问setter方法setContent() 时。这将导致产生一个看起来像Some String Some String more String Some String Some String的随机输出字符串,因为您无法确保必须在more String之后立即添加Some String

该逻辑在您的应用程序中可能会很好。但是,如果仍然需要确保将这两个append语句一起添加到 ,那就是同步到位。在这种情况下,您可以同步setter方法,以确保一次只能有一个线程可以访问它。

不要担心拥有null StringBuilder,因为如果不先实例化MyClass实例就不会访问您的方法。

希望这会有所帮助

答案 2 :(得分:0)

不可变性和线程安全性并存,如果您能够使您的类不可变,那么它说该类本质上是线程安全的,但是要实现这一点并不容易。

要在Java中创建不可变类,您必须执行以下步骤。

  1. 将课程声明为最终课程,以使其无法扩展。
  2. 将所有字段设为私有,以便不允许直接访问。
  3. 不提供变量的setter方法
  4. 将所有可变字段定为最终值,以便可以分配其值 只有一次。
  5. 通过执行深度复制的构造函数初始化所有字段。
  6. 在getter方法中执行对象的克隆以返回副本 而不是返回实际的对象引用。

但是请务必阅读https://dzone.com/articles/do-immutability-really-means,因为这为不变类的思考提供了食物