Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/393.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 类在实例初始化时是线程安全的吗_Java_Multithreading - Fatal编程技术网

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中创建不可变类,必须执行以下步骤

  • 将该类声明为final,以使其无法扩展
  • 将所有字段设置为私有,以便不允许直接访问
  • 不要为变量提供setter方法
  • 将所有可变字段设为最终字段,以便可以为其赋值 只有一次
  • 通过执行深度复制的构造函数初始化所有字段
  • 在getter方法中执行对象克隆以返回副本 而不是返回实际的对象引用
  • 但是一定要读这篇文章,因为它提供了一个关于不可变类的思想食粮

    我的理解是,
    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
    后,无需安全发布。这可能重要,也可能不重要,但这是一个区别。