2011-04-29 40 views
1

比方说,我有这个类:无需​​访问Zoo对象我是否需要在Java中同步对不可变类型的访问?

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

能有一个线程写入这些领域,另一种阅读,?

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

编辑

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

+1

什么使这些值”不可变“?如果你可以写信给他们,他们需要通过某种形式的同步来保护。 – 2011-04-29 22:02:59

+1

@Rodney Gitzel:我认为它的含义是* objects *本身是不可变的,你不能持有对'String '改变内容(你需要创建一个新对象) – 2011-04-29 22:04:45

+1

@Rodney Gitzel String,Double和Integer在Java中都是不可变的类型,做任何改变它们的工作都会返回一个新的对象 – pickypg 2011-04-29 22:04:46

回答

6

从技术上讲,你需要让他们finalvolatile或阅读使用synchronzied保证读者会读的最先进的最新值写入其中。就像你现在所做的那样,如果一个线程写入一个值,那么不能保证另一个线程将读取相同的值。这是因为阅读线程可能会看到缓存值。这对于多核CPU和各种级别的缓存更有可能。

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

+0

'volatile'或'synchronized'是防止两个不同线程同时从同一线程获取不同值的关键。 – pickypg 2011-04-29 22:08:58

+1

我可以建议您在枚举的替代项中提及java.util.concurrent.atomic包中的类吗?这是另一种没有任何明确同步的方式。 – 2011-04-30 04:03:11

0

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

protected Integer monkeysLeft; 
protected Integer monkeysEatenByBears; 

其中两个变量在逻辑上连接,您需要同步访问它们对。

+0

无论如何,如果一个线程正在给他们写信,并且另一个线程正在按照他的建议读取它们,他们就需要同步。 – Finbarr 2011-04-29 22:09:39

1

正如你所说的那样,同一个包中的另一个类可以改变这些值。这个类不是不可变的。现在,如果你不喜欢的东西

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

那么类将是不可改变的。如果你的程序的逻辑把这个类视为不可变的,那么为什么不把它变成不可变的?

此外,如果多个线程检查并更新相同的值,那么你可能会有问题。假设多个线程正在检查和更新monkeyCount,那么monkeyCount会有一个很好的机会,因为没有任何东西会迫使这些检查和更新以原子方式发生。

4

访问和更新与任何类型的字段相对应的存储器单元,除了long或double以外,都保证为原子的(see Concurrent Programming In Java)。这就是为什么人们可能会希望你不需要同步对你的字段的读取权限。但是,Java内存模型允许线程缓存先前读取的值,以防您重复访问它们,因此您应该将这些字段标记为volatile,以确保每个线程都能看到最新的值。

如果您确定没有人会更改字段的值,请将其作为最终值。在这种情况下,不需要易失性字段。

如果字段的值相互依赖,情况就不同了。在这种情况下,我建议使用同步的setter,以确保不会违反类的不变量。

+0

'访问和更新与任何类型的字段相对应的存储器单元,除了long或double以外,都保证是原子的 - 真的吗?我从来没有遇到过任何记录这件事。 – Finbarr 2011-04-29 22:13:15

+0

对不起,忘了提及来源。它来自Doug Leas的'Java中的并发编程' – 2011-04-29 22:21:41

+0

@Finbarr @Sebastian Zarnekow实际上,这来自[Java语言规范,第17.7节双原子和非原子的非原子处理](http://java.sun.com/docs/书籍/ JLS/third_edition/HTML/memory.html#17.7)。 – 2011-04-30 03:51:42

1

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

相关问题