【问题标题】:Is it necessary to synchronize blocks in a constructor?是否有必要在构造函数中同步块?
【发布时间】:2020-08-21 00:21:53
【问题描述】:

这个问题没有回答必要性部分: Synchronized blocks in constructors.

鉴于此格言

  • JVM 一次不允许多个线程调用类的构造函数

问题 A

这是否意味着......?

  1. JVM 将不允许多个线程调用一个类的任何构造函数
    • 这样即使一个类中有多个构造函数,一个类一次也只能由一个线程实例化。
  2. 如果一个类有多个构造函数,那么每个构造函数一次只能被一个线程调用
    • 这样,如果一个类具有三个构造函数,则三个线程可以同时实例化该类。

在我看来,#1 是问题 1 的答案。

问题 B

如果 QuestionA.answer#1 为真,是否意味着在构造函数中使用同步块毫无意义?

例如假设setBrandAccessor方法只被构造函数调用,是否不需要同步setBrandAccessor方法?如有必要,请说明原因。

class DataAccessor {
  static Brand brandAccesor;

  DataAccessor(Brand brand) {
    super(brand);
    setBrandAccessor(Brand brand);
  }
  
  private synchronized setBrandAccessor(Brand brand) {
    if (brandAccessor==null) brandAccessor=brand;
  }
}

【问题讨论】:

  • 这个格言是从哪里来的?

标签: java multithreading constructor


【解决方案1】:

JVM 一次不允许多个线程调用一个类的构造函数

这不是真的,多个线程可以同时调用构造函数。我不知道你从哪里得到这个格言。

问题一

两者都不是,格言本身是不正确的。

问题 B

这不会做你认为它会做的事情。 setBrandAccessor 将在 this 上同步,这对于每个构造函数都不同,因此该方法不会以您期望的方式同步,实际上与根本不同步它是一样的。你可以这样做:

class DataAccessor {
  static Object lock = new Object();
  static Brand brandAccesor;

  DataAccessor(Brand brand) {
    super(brand);
    synchronized(lock) {
        if (brandAccessor==null) brandAccessor=brand;
    }
  }
}

class DataAccessor {
  static Brand brandAccesor;

  DataAccessor(Brand brand) {
    super(brand);
    setBrandAccessor(Brand brand);
  }
  
  // Being static synchronized is the same as synchronizing on a static object
  private static synchronized setBrandAccessor(Brand brand) {
    if (brandAccessor==null) brandAccessor=brand;
  }
}

不过,这确实有点像反模式。如果你不能比这更好地构建你的代码,我会感到惊讶,但是如果没有更多的上下文很难评论。

【讨论】:

  • 不要添加锁对象,只需将setBrandAccessor() 也设为静态即可。
  • @AndrewHenle 添加为替代答案
【解决方案2】:

问题A:两个线程可以同时构造一个对象,并行运行构造函数。两个线程不能同时构造同一个对象。这两个线程将构造两个不同的对象。构造函数在两个不同的对象上运行,这消除了对同步块的最大需求之一。

问题 B:构造函数中的同步块并非毫无意义:构造函数可以访问其他类或静态变量中的可变共享状态,同步块是保证互斥的一种方式。

关于setBrandAccessor in(注意我去掉了static修饰符,使该字段成为实例字段):

class DataAccessor {
  Brand brandAccesor;

  DataAccessor(Brand brand) {
    super(brand);
    setBrandAccessor(brand);
  }
  
  private synchronized setBrandAccessor(Brand brand) {
    brandAccessor=brand;
  }
}

如果您在其他线程中使用此类的对象,则可能需要使方法同步。您需要保证在正在写入的字段和在另一个线程中读取的字段之间存在“发生在”边缘,无论“写入”是否发生在构造函数中。使 getset 方法同步可确保正确的多线程可见性。

保证多线程可见性的另一种方法是删除 set 方法并设置字段final

  final Brand brandAccesor;

  DataAccessor(Brand brand) {
    super(brand);
    brandAccessor=brand;
  }

【讨论】:

    猜你喜欢
    • 2011-06-17
    • 2021-12-30
    • 2019-03-23
    • 1970-01-01
    • 2014-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    相关资源
    最近更新 更多