Java的。如何正确同步getter和setter?

时间:2010-06-19 15:24:15

标签: java synchronization

如果我在一个对象中有多个可变属性,将由多个线程执行,我知道它们应该同步。

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; }

}

问题:

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

5 个答案:

答案 0 :(得分:10)

  
      
  • 不是Java中的返回和赋值原子操作吗?
  •   

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

  • 当读取和写入位于同一个线程中时,读取保证会看到先前的写入。

  • 当读写在不同的线程中时,如果两个线程正确同步,或者如果属性声明为volatile,则只能保证读取先前的写入。

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

  
      
  • 由于属性可能不一定是相互关联的,因此与同一个锁同步并不总是有意义的。如何组织锁定结构?
  •   

如果(并且仅在)锁定争用可能的情况下使用多个锁是有意义的。在您的示例中,如果某些Doggie实例接收到非常高的get和/或set操作率,则锁定争用可能只是一个问题。

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

这取决于。如果您的应用程序正在使用Doggie对象的原始锁,那么您可能会遇到锁争用甚至意外锁定get和set操作。在这种情况下,建议使用私人锁。否则,私有锁是一种不必要的开销。

答案 1 :(得分:5)

答案 2 :(得分:3)

使用引用的操作是原子的,但不是易变的 - 您将始终看到旧值或新值,但不能保证在没有某种内存障碍的情况下您将看到新值。我不记得保证哪些原语是原子的细节 - 可能只有长和双。

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

你肯定需要一个线程安全的可变类型吗?如果你能避免这种要求,它会让生活更简单。

答案 3 :(得分:1)

  • 它们是原子操作,但描绘了两个客户端试图同时获取和设置一个数据的场景。无法确定将要调用哪些订单会对您的应用程序的结果产生很大影响。 (典型的例子是货币交易。)
  • 与同一个锁同步可能有意义也可能没有意义 - 这实际上取决于您的应用程序。但是,如果没有必要,将整个对象锁定在一起并不是一个好主意。
  • 与Jon所说的一样,从一个私人锁开始,然后根据结果从那里开始。

答案 4 :(得分:0)

您应该注意,非相关属性可以具有不同的锁定。考虑到锁定对象需要琐碎的内存,我个人会使用每个属性的锁而不是整个对象的一个​​锁。

这样做的轻量级方法就是在写入属性时设置一个布尔值,否则清除。执行此操作,支持超时等的重量级方法是使用互斥锁。