Java 在一个有许多实例的类中,使用同步还是为字段使用原子变量更好?

Java 在一个有许多实例的类中,使用同步还是为字段使用原子变量更好?,java,multithreading,Java,Multithreading,我正在编写一个类,其中将创建许多实例。多个线程将使用这些实例,因此类字段的getter和setter必须是并发的。这些字段主要是浮动字段。问题是,我不知道什么更需要资源;使用同步部分,或者使变量类似于AtomicInteger 如果可能的话,您应该喜欢原子原语。在许多体系结构上,原子原语可以执行得更好,因为更新它们的指令可以完全在用户空间中执行;我认为synchronized块和Locks通常需要操作系统内核的一些支持才能工作 请注意我的警告:“如果可能的话”。如果类的操作需要一次以原子方式更新

我正在编写一个类,其中将创建许多实例。多个线程将使用这些实例,因此类字段的getter和setter必须是并发的。这些字段主要是浮动字段。问题是,我不知道什么更需要资源;使用同步部分,或者使变量类似于AtomicInteger

如果可能的话,您应该喜欢原子原语。在许多体系结构上,原子原语可以执行得更好,因为更新它们的指令可以完全在用户空间中执行;我认为
synchronized
块和
Lock
s通常需要操作系统内核的一些支持才能工作


请注意我的警告:“如果可能的话”。如果类的操作需要一次以原子方式更新多个字段,则不能使用原子原语。例如,如果一个类必须修改一个集合并更新一个计数器(例如),那么单靠原子原语是无法实现的,因此您必须使用
同步的
或一些
锁定的

在实例被多个线程使用之前,正确地构造它们是非常重要的。否则,这些线程将从这些部分构造的实例中获取不完整或错误的数据。我个人的偏好是使用同步块。 或者,您也可以遵循Brain Goetz在其著作《Java并发实践》中概述的“惰性初始化持有者类习惯用法”:

在这里,JVM延迟初始化ResourceHolder类,直到它被实际使用。而且,资源是用静态初始值设定项初始化的,不需要额外的同步


注意:静态初始化的对象在构造期间或被引用时都不需要显式同步。但是,如果对象是可变的,读者和作者仍然需要同步,以使后续修改可见,并避免数据损坏。

这个问题已经有了一个公认的答案,但由于我还不允许写评论,我们开始吧。我的回答是这要看情况而定。如果这很关键,请测量。JVM非常擅长在没有(或很少)争用的情况下优化同步访问,这比每次都要使用真正的内核互斥体要便宜得多。原子学基本上使用自旋锁,这意味着它们将尝试进行原子改变,如果失败,它们将一次又一次地尝试,直到成功。这可能会消耗相当多的CPU,因为许多线程都在激烈争夺资源

使用低争用原子可能是一个不错的选择,但为了确保这两种方法都适用于您的预期应用


我可能会从同步方法开始,以保持代码简单;然后测量并改变原子,如果它有区别。

为什么不让实例不变?@trashgood:它们代表3D场景中的对象;他们将改变很多。不变性不允许更改此类值,除非您为每次更改生成一个新对象。同步块不仅锁定并需要操作系统资源,原子原语通常表现得更好,因为它们根本不锁定。@spatulamania:所以对于每个同步的变量,都会在硬件/OS/C级别上产生一个新的关键部分?我不确定关键部分存在于哪个级别(VM或OS)。这是针对每个同步块的,不是变量。原子原语OTOH不使用关键部分。@spatulamania,Sun JVM创建的线程是OS(内核)线程,而不是“绿色”(用户)线程,因此互斥构造由OS内核(无论是什么操作系统)提供。@MikeDaniels很高兴知道。谢谢:-)
@ThreadSafe
public class ResourceFactory {
  private static class ResourceHolder {
    public static Resource resource = new Resource();
  }

  public static Resource getResource() {
    return ResourceHolder.resource;
  }
}