我是否需要在Singleton类中使每个方法同步?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我是否需要在Singleton类中使每个方法同步?相关的知识,希望对你有一定的参考价值。
在下面的多线程编程示例中,Replacer
类可以同时被多个线程访问,因此我使类单例并使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);
}
}
对于单身人士来说,这条规则并不特别。这纯粹是:
- 该方法是否需要支持多个调用它的线程,以及
- 如果多个线程在Just The Wrong Time调用它,它会做什么失败
你的replaceNum
的答案是“是的,它需要同步”(并且在方法级别是有意义的,因为它内部的所有内容都需要同步)因为它使用ArrayList
的方法,这不是线程安全的。作为the Javadoc says:
请注意,此实现不同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)
所以该方法需要同步访问该ArrayList
。
鉴于这样的一揽子声明,你必须假设这些方法都不是线程安全的,除非它明确说明它是。 (同样,除非一个类明确表示它是线程安全的,否则你必须假设它不是。)
(答案是为an earlier version of the question写的)。
不,您不需要使其同步,因为您从不在结构上更改列表。
如果列表不为空,则只添加到列表中。所以你永远不会添加第一个元素。
这几乎肯定是一个逻辑缺陷,但它回答了所写的问题。
假设您修复了逻辑,并且可以向列表添加元素,并且您想要从多个线程调用它,是的,它应该是同步的。
但其原因并不是ArrayList
明确记录为从多个线程访问时需要同步(尽管表明必须以某种方式涉及同步):
请注意,此实现不同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)
这是您在列表上执行复合操作:如果列表不为空,则删除其最后一个元素;然后添加一个新元素。
第一个位(如果列表不为空,删除其最后一个元素)可能被多个线程弄乱。例如,两个线程可以同时找到列表大小为1;然后一个线程将另一个元素移到另一个元素之前;然后另一个线程试图删除一个现在删除的元素。 KABOOM。
或者,如果两个线程都发现列表为空,则添加可能会混乱,因此不要尝试删除任何内容;然后两者都添加,你最终得到列表中的两个元素。
即使您使用Collections.synchronizedList
,如ArrayList
文档中的建议,这也不能解决您的问题。重要的是要认识到上面的引用是你必须做的,以保持ArrayList
的不变量;它不会强制执行代码的不变量。
您需要能够以原子方式执行此逻辑。整个过程就像一个“单一动作”,由一个独立访问列表的线程完成。这是通过同步完成的。
任何更改共享实例状态的方法都应标记为synchronized
以确保安全。 synchronized
方法“检出”对类的实例(this
)的锁定,并且所有同步方法都控制同一个锁,因此在多个方法上使用此关键字将完成这项工作。虽然,如果你希望它被多次调用,你应该尝试在一小段代码上使用synchronize
。通常,您应该尽可能少地同步代码以获得最佳性能。
如果对方法调用(写入和读取)有多次访问,那么,是的,您需要使用synchronized
,因为ArrayList
本身并不是线程安全的实现。
就像是:
public synchronized void replaceNum(List<Integer> newNums) {
if (nums.size() > 0) {
nums.remove(nums.size() - 1);
}
nums.addAll(newNums);
}
另一个选择是你可以使用Vector
代替,这使你的实现线程安全。但是,它有自己的性能问题。通常,与非线程安全的对应程序相比,线程安全实现的性能较慢。
以上是关于我是否需要在Singleton类中使每个方法同步?的主要内容,如果未能解决你的问题,请参考以下文章