我是否需要在Java中同步对不可变类型的访问?

假设我有这个课程:

class Zoo { protected String bearName; protected Double trainerSalary; protected Integer monkeyCount; } 

一个线程可以写入这些字段,而另一个线程可以读取它们,而无需synchronized访问Zoo对象吗?

注意:这些值可以彼此分开处理,因此在trainerSalary更改trainerSalary并不重要。

编辑

只是为了澄清,这些领域是可变的 ; 只有他们引用的对象是不可变的

从技术上讲,你需要使它们finalvolatile或者使用synchronzied来读取写入它们,以保证读者能够读取最新的值。 正如你现在所拥有的,如果一个线程写入一个值,则无法保证另一个线程将读取相同的值。 这是因为阅读线程可能会看到缓存值。 这更有可能是多核CPU和各种级别的缓存。

一本关于此的好书是Java Concurrency in Practice 。

对除了long或double之外的任何类型的字段对应的存储器单元的访问和更新都保证是primefaces的(参见Java中的并发编程) 。 这就是为什么人们可能期望您不需要同步对字段的读取权限。 但是,Java内存模型允许线程缓存先前读取的值,以防您重复访问它们,因此您应将字段标记为volatile,以确保每个线程都能看到最新的值。

如果您确定没有人会更改字段的值,请将它们设为最终值。 在这种情况下,不需要挥发场。

如果字段的值彼此依赖,则情况会有所不同。 在这种情况下,我建议使用同步设置器,以确保不违反类的不变量。

正如您所说的那样,同一个包中的另一个类可以更改这些值。 这个类不是不可变的。

现在,如果你做了类似的事情

 class Zoo { protected final String bearName; protected final Double trainerSalary; protected final Integer monkeyCount; } 

然后这个类将是不可变的。 如果程序的逻辑将此类视为不可变,那么为什么不使它实际上不可变?

此外,如果多个线程正在检查并更新相同的值,那么您可能会遇到问题。 假设多个线程正在检查并更新monkeyCount,那么很有可能monkeyCount最终会出错,因为没有什么能够强制执行这些检查并且更新以primefaces方式发生。

我的2美分,来自“Java编程语言”,第4版,14.10.2:“有一种常见的误解,即对不可变对象的共享访问不需要任何同步,因为对象的状态永远不会改变。这是一种误解一般来说,因为它依赖于一个线程将保证看到不可变对象的初始化状态的假设,并且不一定是这样。问题是,虽然共享对象是不可变的,但是用于访问共享对象本身是共享的并且通常是可变的 – 因此,正确同步的程序必须同步对该共享引用的访问,但是程序通常不会这样做,因为程序员不认识到需要这样做。例如,假设一个线程创建了一个String对象并在静态字段中存储对它的引用。然后第二个线程使用该引用来访问该字符串。根据我们到目前为止所讨论的内容,无法保证由t写入的值 第一个线程在构造字符串时,第二个线程在访问字符串时会看到它。“

如果这些变量确实是独立的,那么不,你不需要同步。 但是,如你所知,如果有的话

 protected Integer monkeysLeft; protected Integer monkeysEatenByBears; 

在这两个变量逻辑连接的情况下,您需要同步访问它们。