0

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

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

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 个锁吗?我想知道是否有另一种方法可以在不过度锁定的情况下实现这一目标。

4

2 回答 2

4

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

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

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

其他几条评论:

  • 如果您可以只使用volatile字段,那么您不需要任何synchronized块。
  • 如果您有synchronized方法,那么您不需要创建您的字段volatile
  • 如果该name字段可能应该被标记为final好像它没有被写入。
于 2012-10-02T23:14:02.047 回答
4

如果您担心同步原始类型,这是 AtomicInteger 等的绝佳用例......它们非常快并确保线程安全。欲了解更多信息:

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

于 2012-10-02T23:16:13.360 回答