如何单独同步数据成员

时间:2012-10-02 23:11:34

标签: java multithreading synchronization thread-safety synchronized

我一直在网上搜索这个,但一直找不到任何接近此文章的文章,我对此感到非常惊讶。也许智慧隐藏在我尚未找到的地方。

假设我有一个包含10个不同类型成员的类(为了简单起见,假设它们混合了int和Strings),并且每个类都有自己的访问器方法。现在,我想让这个类线程安全。但是,其中一些数据成员不一定互相交互。例如,下面的课程Person包含agename以及其他属性。

public class Person {
    private volatile int age;
    private String name;
    private volatile long blabla;
    // ... and so on

    public synchronized int getAge() {
        return age;
    }

    public synchronized void setAge(int age) {
        this.age = age;
    }

    // .. and so on for each data member
}

一个线程可能只需要读/写age,而其他线程只需要修改name。显然,将synchronized添加到每个访问器方法是一个坏主意,因为它锁定了对象的整个实例。正在调用getAge()的线程必须等待另一个正在调用getName()的线程,即使agename是两个单独的字段。

因此,一个明显的解决方案是为每个字段创建一个锁(或将volatile添加到基本类型)。然而,这似乎是一种矫枉过正。如果我有10个数据成员,我还需要10个锁吗?我想知道是否有另一种方法可以在没有过多锁定的情况下实现这一点。

2 个答案:

答案 0 :(得分:4)

首先,如果您正在谈论基元(或String等不可变对象),那么您应该只需要标记每个字段volatile。如果您所做的只是获取和设置字段值,则无需锁定。

但是,如果你的get / set方法执行多个操作并且需要synchronized块,那么每个字段有synchronized个块似乎对我来说过早优化。我认为像synchronized这样的小对象上的Person方法是完成此任务的完美方法。除非你有真正的理由(即分析器输出),否则我不会试图让它更复杂。 当然每个字段的锁定在任何情况下都是过度的。

如果该方法需要很长时间,那将会有所不同。然后你想要锁定整个对象并阻止其他访问者。那么现在是拥有多个锁的好时机 - 每个锁用于单独计算。但是如果你的对象真的只是想保护get / set,那么synchronized方法就可以了。

其他几条评论:

  • 如果您只使用volatile字段,则不需要任何synchronized块。
  • 如果您使用synchronized方法,则无需创建字段volatile
  • 如果name字段未被写入,则可能应标记为final

答案 1 :(得分:4)

如果您担心同步基元类型,这是AtomicInteger等的一个很好的用例......它们非常快并且确保线程安全。欲了解更多信息:

http://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html