不可变对象是线程安全的,但为什么呢?

Tar*_*ngh 22 java

如果一个线程通过创建其对象来创建填充不可变类的引用变量,并且在第一个线程完成之前第二次另一个线程启动并创建不可变类的另一个对象,则不可变类的使用不会是线程不安全?创建一个不可变对象也表示所有字段都标记为final ....."如果对新创建的实例的引用从一个线程传递到另一个线程而没有同步,则可能需要确保正确的行为"他们是否正在尝试假设另一个线程可能会将引用变量重新指向不可变类的某个其他对象,那么线程将指向不同的对象而使状态不一致?

Ama*_*eep 16

实际上,不可变对象总是线程安全的,但它的引用可能不是.

困惑??你不应该: -

回到基本:线程安全只是意味着两个或多个线程必须协同工作在共享资源或对象上.他们不应该忽略任何其他线程所做的更改.

现在String是一个不可变类,只要一个线程试图改变它,它就会最终创建一个新对象.因此,即使是相同的线程也不能对原始对象进行任何更改,并且谈论另一个线程就像去Sun一样,但是这里的问题是通常我们使用相同的旧引用来指向新创建的对象.

当我们执行代码时,我们仅使用引用来评估对象中的任何更改.

声明1:String str ="123"; //最初将字符串共享到两个线程

声明2:str = str +"FirstThread"; //由第一个线程执行

声明3:str = str +"SecondThread"; //由第二个线程执行

现在由于没有同步,易失或最终关键字告诉编译器跳过使用其智能进行优化(任何重新排序或缓存的东西),这个代码可以按以下方式运行.

  1. 加载Statement2,所以str ="123"+"FirstThread"
  2. 加载Statement3,所以str ="123"+"SecondThread"
  3. 存储Statement3,所以str ="123SecondThread"
  4. 存储Statement2,所以str ="123FirstThread"

最后参考str ="123FirstThread"中的值,如果我们假设幸运的是我们的GC线程正在休眠,那么我们的不可变对象在我们的字符串池中仍然不存在.

因此,Immutable对象始终是线程安全的,但它们的引用可能不是.为了使它们的引用成为线程安全的,我们可能需要从synchronized块/方法中访问它们.


mer*_*ike 9

不可变对象是线程安全的,但为什么呢?

不可变对象是一旦构造完就不再被修改的对象.此外,不可变对象只有在构造完成后才能被其他线程访问,并且这是通过正确的同步完成的,所有线程都将看到对象的相同有效状态.

如果一个线程通过创建其对象来创建填充不可变类的引用变量,并且在第一个线程完成之前第二次另一个线程启动并创建不可变类的另一个对象,则不可变类的使用不会是线程不安全?

不,是什么让你这么认为?对象的线程安全性完全不受您对同一类的其他对象所做的操作的影响.

他们是否试图说另一个线程可能会将引用变量重新指向不可变类的某个其他对象,那么线程将指向不同的对象而使状态不一致?

他们试图说,无论何时将某个东西从一个线程传递到另一个线程,即使它只是对不可变对象的引用,也需要同步线程.(例如,如果通过将引用存储在对象或静态字段中将引用从一个线程传递到另一个线程,则该对象或字段由多个线程访问,并且必须是线程安全的)

  • 请注意,通过仅使用final字段可以使关于"仅在构造之后可访问"的部分变得微不足道(并且因为对象是不可变的,没有理由不这样做).如果我们避免一些明显的错误,java内存模型提供了我们需要的所有[保证](http://java.sun.com/docs/books/jls/third_edition/html/memory.html#66562). (2认同)

Bhu*_*han 8

除了已经发布的其他答案,一旦创建了不可变对象,它们就无法进一步修改.因此它们基本上是只读的.

众所周知,只读的东西总是线程安全的.即使在数据库中,多个查询也可以同时读取相同的行,但是如果要修改某些内容,则需要对其进行独占锁定.


Alb*_*rez 6

线程安全是数据共享的安全性,因为在您的代码中,您可以根据对象所拥有的数据做出决策,因此它的完整性和确定性行为至关重要.即

想象一下,我们在两个线程上有一个共享的布尔实例变量,这两个线程即将使用以下逻辑执行一个方法

  • 如果flag为false,那么我打印"false"然后我将标志设置回true.
  • 如果flag为true,那么我打印"true"然后我将标志设置为false.

如果您在单个线程循环中连续运行,您将获得一个确定性输出,如下所示:

false - true - false - true - false - true - false ...

但是,如果你用两个线程运行相同的代码,那么,输出的输出不再是确定性的,原因是线程A可以唤醒,读取标志,看到它是假的,但在它可以做任何事情之前,线程B醒来并读取标志,这也是假的!! 所以两者都会打印错误......这只是我能想到的一个有问题的场景......正如你所看到的,这很糟糕.

如果您取消等式的更新,问题就会消失,只是因为您消除了与数据同步相关的所有风险.这就是为什么我们说不可变对象是线程安全的.

重要的是要注意,不可变对象并不总是解决方案,您可能需要在不同线程之间共享数据,在这种情况下,有许多技术超出了普通同步并且可以创建一个你的应用程序的性能差异很大,但这是一个完全不同的主题.

不可变对象对于保证我们确定不需要更新的应用程序区域不会更新非常重要,因此我们确信我们不会遇到多线程问题

您可能有兴趣看看几本书:

这是最受欢迎的:http://www.amazon.co.uk/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=sr_1_1?ie= UTF8& qid= 1329352696& sr=8-1

但我个人更喜欢这个:http : //www.amazon.co.uk/Concurrency-State-Models-Java-Programs/dp/0470093552/ref=sr_1_3? ie=UTF8&qid=1329352696&sr =8-3

请注意,多线程可能是任何应用程序中最棘手的方面!


小智 0

两个线程不会创建相同的对象,所以没有问题。

关于“可能有必要确保......”,他们所说的是,如果您不将所有字段设为最终字段,您将必须自己确保正确的行为。