2010-06-19 85 views
10

如果我的几个可变属性会被多个线程处理,我知道它们应该同步。Java。如何正确同步getter和setter?

class Doggie { 
    private String name; 
    private int age; 

    public void setName(String name) { this.name = name; } 
    public String getName() { return this.name; } 
    public void setAge(int age) { this.age = age; } 
    public int getAge() { return this.age; } 

} 

问题:

  • 是不是return和分配原子操作在Java中?
  • 由于属性可能不一定是相互关联的,因此与同一个锁同步并不总是有意义的。如何组织锁定结构?
  • 使用内部锁或私有对象锁模式更好吗?
+0

同步会在这里实现什么?即使你可以保证一个线程在它存在的时候拥有了setName(),当它退出时,也没有什么说在第一个线程调用getName()之前另一个线程不能立即做另一个setName()。在任何时候,你永远不会百分之百地确定这个州。 – 2010-06-19 15:33:19

回答

10
  • 不是存在于Java返回和分配原子操作?

是的,他们是原子(在某些情况下,至少),但原子是不是唯一的问题。另一个重要问题是,是否保证一个线程写入属性的动作对于不同线程所做的相同属性的后续读取可见。

  • 当读写操作在同一个线程中时,读操作保证能够看到前面的写操作。

  • 当读取和写入位于不同的线程中时,只有在两个线程同步正确时才能保证读取先前的写入...或者该属性被声明为volatile

请注意,原始锁/互斥不是唯一的同步方法。

  • 由于性能不一定是相互关联的,它并不总是有意义用同一把锁进行同步。如何组织锁定结构?

如果(且仅当)锁定争用可能时,使用多个锁才有意义。在您的示例中,如果某个Doggie实例收到非常高的获取和/或设置操作速率,则锁定争用可能只是一个问题。

  • 使用内部锁或私有对象锁模式更好吗?

这取决于。如果您的应用程序正在使用Doggie对象的原始锁定,则可能会发生锁定争用,甚至无意中锁定get和set操作。在这种情况下,私人锁可能是明智的。否则,私人锁定是不必要的开销。

3

带引用的操作是原子操作,但不是易失性的 - 您将始终看到旧值或新值,但不保证您在没有某种内存障碍的情况下将看到新值。我不记得哪些基元保证是原子的细节 - 可能都是长而双倍的。

就我个人而言,我会使用一个私人锁,直到我看到任何证据表明它是一个瓶颈。我建议不要锁定“this”,因为其他代码也可能会锁定它。如果你是唯一知道锁的代码,那么很难得到干涉。话虽如此,如果调用者想要以原子方式更改多个属性,则可能需要通过属性公开该锁。

你确实需要一个线程安全的可变类型吗?如果你可以避免这个要求,它会使生活变得更简单。

1
  • 它们是原子操作,但图片是两个客户端试图同时获取和设置一段数据的场景。无法保证将调用哪些命令,这可能会大大影响应用程序的结果。 (经典示例是金钱交易。)
  • 与同一锁定同步可能有意义或无意义 - 这取决于您的应用程序。但是,如果没有必要锁定整个对象通常不是一个好主意。
  • 与乔恩所说的一样,从一个单独的私人锁开始,然后根据结果从那里开始。
0

你是对的注意到非相关属性可以有不同的锁。考虑到锁对象需要微不足道的内存,我个人会为每个属性使用一个锁而不是整个对象。

执行此操作的轻量级方法只是在属性正在写入时设置布尔值,否则将清除该属性。重要的方法来做到这一点,以支持超时等,与互斥体。

+0

Java互斥锁不支持超时。 – 2010-06-19 15:38:36

+0

啊,这是一个耻辱.. – Reinderien 2010-06-19 16:17:08

+0

那么,与'synchronized'得到的“内置”互斥体不。如果你想超时,你总是可以明确地使用一个'java.util.concurrent.locks.ReentrantLock',它有'tryLock()'方法有和没有超时。 – 2010-06-19 18:48:02