我是否需要在 Singleton class 中同步每个方法?

Do I need to make every method synchronized in a Singleton class?

在下面的多线程编程例子中,Replacer class 可以同时被多个线程访问,所以我做了 class 单例并做了 getInstance()方法同步。我是否还需要使 replaceNum() 方法同步,假设该方法也将被多个线程调用?

public class Replacer {

  private static Replacer replacer = null;
  private List<Integer> nums;

  private Replacer() {
    nums = new ArrayList<>();
  }

  public static synchronized Replacer getInstance() {
    if (replacer == null) {
      replacer = new Replacer();
    }

    return replacer;
  }

  // Do I need to make below method synchronized?
  public void replaceNum(List<Integer> newNums) {
    if (nums.size() > 0) {
      nums.remove(nums.size() - 1);
    }

    nums.addAll(newNums);
  }
}

这条规则对单身人士来说并不特殊。纯粹是:

  • 该方法是否需要支持多线程调用,
  • 如果多个线程在错误的时间调用它,它会做任何会失败的事情吗

你的 replaceNum 的答案是 "yes, it needs synchronization"(在方法级别上是有意义的,因为基本上其中的所有内容都需要同步)因为它使用 ArrayList' s 方法,这些方法不是线程安全的。作为 the Javadoc says:

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.)

因此该方法需要同步访问 ArrayList

鉴于这样的笼统声明,您必须假设 none 方法是线程安全的,除非它明确说明它 。 (类似地,除非 class 明确说明它 线程安全的,否则您必须假设它不是。)

任何更改共享实例状态的方法都应标记为 synchronized 以确保安全。 synchronized 方法 "check out" class (this) 实例上的锁,所有同步方法都控制同一个锁,因此在多个方法上使用此关键字将做这份工作。虽然,如果您希望它被多次调用,您应该尝试 synchronize 一小段代码。一般来说,您应该同步尽可能少的代码以获得最佳性能。

(答案是为 an earlier version of the question 写的)。

不,您不需要使其同步,因为您从未在结构上更改列表。

如果列表不为空,您只能添加到列表中。所以你永远不会向它添加第一个元素。

这几乎可以肯定是一个逻辑缺陷,但它回答了所写的问题。

假设你固定了逻辑,你可以向列表中添加元素,并且你想从多个线程调用它,是的,它应该是同步的。

但这样做的原因是 而不是 ArrayList 被明确记录为从多个线程访问时需要同步(尽管表明必须以某种方式涉及同步):

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, itmust be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.)

这是你在列表上执行复合操作:如果列表不为空,删除它的最后一个元素;然后添加一个新元素.

第一位(如果列表不为空,删除它的最后一个元素)可能会被多个线程弄乱。例如,两个线程可以同时找到大小为 1 的列表;然后一个线程在另一个线程之前删除最后一个元素;然后另一个线程尝试删除一个现在删除的元素。轰隆隆。

或者,如果两个线程都发现列表为空,则添加可能会被打乱,因此不要尝试删除任何内容;然后两者相加,最后列表中有两个元素。

即使您按照 ArrayList 文档中的建议使用 Collections.synchronizedList,也无法解决您的问题。重要的是要意识到上面的引用是你必须做的来保留 ArrayList 的不变量 ;它不执行你的代码的不变量。

您需要能够自动执行此逻辑。整个事情就像 "single action" 一样,由一个对列表具有独占访问权限的线程完成。这是通过同步来完成的。

如果对方法调用有多个访问(写入和读取),那么,是的,您将需要使用 synchronized,因为 ArrayList 本质上不是线程安全的实现。

类似于:

public synchronized void replaceNum(List<Integer> newNums) {
    if (nums.size() > 0) {
        nums.remove(nums.size() - 1);
    }
    nums.addAll(newNums);
}

另一种选择是您可以改用 Vector,这使您的实现线程安全。但是,它有自己的性能问题。通常,与非线程安全的对应实现相比,线程安全实现的性能较慢。