假设我有这门课程:
class Zoo
{
protected String bearName;
protected Double trainerSalary;
protected Integer monkeyCount;
}
一个线程可以写入这些字段,而另一个线程可以读取它们,而不需要synchronized
访问Zoo
对象吗?
注意:这些值可以彼此分开处理,因此在读取trainerSalary
时更改monkeyCount
并不重要。
修改:
为了澄清,这些字段可变;只有他们引用的对象是不可变的。
答案 0 :(得分:6)
从技术上讲,您需要使用final
,volatile
或阅读并使用synchronzied
来编写它们,以确保读者能够阅读最多的内容 - 日期值。正如你现在所拥有的,如果一个线程写入一个值,则无法保证另一个线程将读取相同的值。这是因为阅读线程可能会看到缓存值。对于多核CPU和各种级别的缓存,这种情况更有可能。
一本关于此的好书是Java Concurrency in Practice。
答案 1 :(得分:4)
对与除long或double之外的任何类型的字段相对应的存储器单元的访问和更新保证是原子(see Concurrent Programming In Java)。这就是为什么人们可能期望您不需要同步对字段的读取权限。但是,Java内存模型允许线程缓存先前读取的值,以防您重复访问它们,因此您应将字段标记为volatile,以确保每个线程都能看到最新的值。
如果您确定没有人会更改字段的值,请将其设为最终。在这种情况下,不需要挥发场。
如果字段的值相互依赖,则情况会有所不同。在这种情况下,我建议使用同步设置器,以确保不违反类的不变量。
答案 2 :(得分:1)
正如您所说的那样,同一个包中的另一个类可以更改这些值。这个类不是一成不变的。
现在,如果你做了类似
的事情class Zoo
{
protected final String bearName;
protected final Double trainerSalary;
protected final Integer monkeyCount;
}
然后这个类将是不可变的。如果程序的逻辑将此类视为不可变,那么为什么不使它实际上不可变?
此外,如果多个线程正在检查并更新相同的值,那么您可能会遇到问题。假设多个线程正在检查并更新monkeyCount,那么很有可能monkeyCount最终会变得不正确,因为没有什么能够强制执行这些检查并且更新以原子方式发生。
答案 3 :(得分:1)
我的2美分,来自“Java编程语言”,第4版,14.10.2: “存在一种常见的误解,即对不可变对象的共享访问不需要任何同步,因为对象的状态永远不会改变。这通常是一种误解,因为它依赖于假设线程将保证看到 不可变对象的初始化状态,并且不一定是这种情况。问题在于,虽然共享对象是不可变的,但用于访问共享对象的引用本身是共享的并且通常是可变的 - 因此,正确同步的程序必须同步对该共享引用的访问,但通常程序不会这样做,因为程序员不承认这样做的必要性。例如,假设一个线程创建一个String对象并在静态字段中存储对它的引用。然后第二个线程使用它 引用访问字符串。根据我们到目前为止所讨论的内容,无法保证第一个线程在构造字符串时写入的值在访问字符串时将被第二个线程看到。“
答案 4 :(得分:0)
如果这些变量确实是独立的,那么不,你不需要同步。但是,正如您所说,如果您有
protected Integer monkeysLeft;
protected Integer monkeysEatenByBears;
这两个变量在逻辑上是连接的,你需要同步访问它们。