我一直试图理解多线程的概念,并对以下代码感到困惑:
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对象时,内容引用可能具有不正确的对象初始化。为了进行适当的初始化,内容应为最终内容。 谁能帮我澄清一下?
答案 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中创建不可变类,您必须执行以下步骤。
但是请务必阅读https://dzone.com/articles/do-immutability-really-means,因为这为不变类的思考提供了食物