Java 类在实例初始化时是线程安全的吗
我一直试图理解多线程的概念,但对以下代码感到困惑:Java 类在实例初始化时是线程安全的吗,java,multithreading,Java,Multithreading,我一直试图理解多线程的概念,但对以下代码感到困惑: class MyClass{ private StringBuilder content = new StringBuilder(); public void setContent(){ content.append("Some String"); content.append("more String"); } public String getContent
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对象时,内容引用可能具有不正确的对象初始化。为了进行适当的初始化,内容应该是最终的。
有人能帮我澄清一下吗 不可变性和线程安全性是相辅相成的,如果你能使你的类不可变,那么它说这个类天生就是线程安全的,但是要实现同样的目标并不容易 要在java中创建不可变类,必须执行以下步骤
MyClass
不能仅仅通过同步其setter和getter方法来实现线程安全
这是不对的
如果MyClass
实例的引用被安全地发布到所有使用它的线程,那么synchronized
getter和setter将看到对象的正确初始状态
如果您将内容
(以及任何其他字段)声明为最终
,则可以免除安全发布的要求。但是,由于这不是一个不可变的类,因此getter和setter仍然需要同步
final
字段的特殊语义(如JLS 17.5中所述)允许真正不可变的对象是线程安全的,而不会产生任何同步开销。但是这些语义在您的示例中并不直接适用,因为“setter”正在改变对象
顺便说一下,如果
content
具有类型StringBuffer
,而不是StringBuilder
,并且变量是final
。结果将是“大部分”线程安全的,而无需同步
。这是因为StringBuilder
对于这些操作是线程安全的。唯一的问题是您的“setter”调用了两次append
。如果没有同步的,getter可能会看到缓冲区的中间状态。不变性并不总是线程安全的答案。让我们首先检查现有代码中潜在的线程安全问题。潜在的问题是当两个线程(A和B)同时访问setter方法setContent()
时。这将导致产生一个随机输出字符串,该字符串可能看起来像Some String Some String more String Some String Some String
,因为您无法确保必须在Some String
之后添加more String
这种逻辑在您的应用程序中可能是完美的。但是,如果您仍然需要确保将这两个append语句添加到一起,那么就需要进行同步。在这种情况下,您可以同步setter方法,以确保一次只有一个线程可以访问它
不要担心null
StringBuilder
,因为如果不先实例化MyClass
实例,将无法访问您的方法
希望这有助于使类成为单例,并使setter和getter同步。这肯定是设计类的另一种方法。我想得到的是正确的理解,这个类是否是线程安全的。依我看不是。是的<代码>内容应该是最终的,以保证所有线程都能看到其初始化值。当前该类不是线程安全的。非常感谢@Andy Turner。我不同意。更改
content
的内容会从调用getContent()
的对象的角度更改MyClass
的状态。它是一个可变类。但这不是我想说的重点。添加final
后,无需安全发布。这可能重要,也可能不重要,但这是一个区别。