【问题标题】:How to individually synchronize data members如何单独同步数据成员
【发布时间】:2012-10-02 23:11:34
【问题描述】:

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

假设我有一个包含 10 个不同类型成员的类(为了简单起见,假设它们混合了整数和字符串),并且每个成员都有自己的访问器方法。现在,我想让这个类线程安全。但是,其中一些数据成员不一定会相互交互。比如下面的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 个锁吗?我想知道是否有另一种方法可以在不过度锁定的情况下实现这一点。

【问题讨论】:

  • 如果您的方法是同步的并且成员是私有的,则不需要volatile

标签: java multithreading synchronization thread-safety synchronized


【解决方案1】:

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

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

【讨论】:

  • 虽然我同意 Gray 的观点,这听起来像是过早的优化,但了解原子变量及其使用方式总是很好的。对于直接、并发的变量访问/修改非常有用。 +1
【解决方案2】:

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

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

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

其他几个cmets:

  • 如果您可以只使用 volatile 字段,那么您不需要任何 synchronized 块。
  • 如果您有 synchronized 方法,则无需将字段设为 volatile
  • 如果name 字段没有被写入,则可能应该标记为final

【讨论】:

  • 是小物件/大物件有关系吗?我认为不是,但我明白你的意思。
  • 没关系@iccthedral。 重要 是所讨论的方法是否需要很长时间才能进行计算。如果它们是真正的 get/set 方法,那么对象的整体大小就无关紧要了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多