java - 用一个扭曲的方式实现单例模式
问题描述
这是一个求职面试问题。
用一个扭曲的方式实现单例模式。首先,不是存储一个实例,而是存储两个实例。在每个偶数调用中
getInstance()
,返回第一个实例,在每个奇数调用中getInstance()
,返回第二个实例。
我的实现如下:
public final class Singleton implements Cloneable, Serializable {
private static final long serialVersionUID = 42L;
private static Singleton evenInstance;
private static Singleton oddInstance;
private static AtomicInteger counter = new AtomicInteger(1);
private Singleton() {
// Safeguard against reflection
if (evenInstance != null || oddInstance != null) {
throw new RuntimeException("Use getInstance() instead");
}
}
public static Singleton getInstance() {
boolean even = counter.getAndIncrement() % 2 == 0;
// Make thread safe
if (even && evenInstance == null) {
synchronized (Singleton.class) {
if (evenInstance == null) {
evenInstance = new Singleton();
}
}
} else if (!even && oddInstance == null) {
synchronized (Singleton.class) {
if (oddInstance == null) {
oddInstance = new Singleton();
}
}
}
return even ? evenInstance : oddInstance;
}
// Make singleton from deserializaion
protected Singleton readResolve() {
return getInstance();
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Use getInstance() instead");
}
}
你看到问题了吗?第一个调用可能进入getInstance
并且线程被抢占。然后第二个调用可能会进入getInstance
,但会得到.oddInstance
而不是evenInstance
.
显然,这可以通过使getInstance
同步来防止,但它是不必要的。在单例的生命周期中只需要同步两次,而不是每次getInstance
调用。
想法?
解决方案
最重要的是,需要声明evenInstance
and变量。参见著名的“Double-Checked Locking is Broken”声明:https ://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmloddInstance
volatile
此外,您应该在同步块中为偶数和奇数实例使用不同的对象,以便可以同时构造它们。
最后,Singleton
构造函数中的检查被打破,并在第二次调用中抛出异常getInstance()
除此之外它很好,但如果你不自己做并发工作会更好:
public final class Singleton implements Cloneable, Serializable {
private static AtomicInteger counter = new AtomicInteger(1);
public static Singleton getInstance() {
if (counter.getAndIncrement() % 2 == 0) {
return EvenHelper.instance;
} else {
return OddHelper.instance;
}
}
private static class EvenHelper {
//not initialized until the class is used in getInstance()
static Singleton instance = new Singleton();
}
private static class OddHelper {
//not initialized until the class is used in getInstance()
static Singleton instance = new Singleton();
}
}
推荐阅读
- mysql - 具有动态列名的 SQL SELECT 语句
- mysql - 无法在 10.1.34-MariaDB 中使用 WITH AS
- android - 致命异常:java.lang.UnsatisfiedLinkError 找不到 lib.so
- javascript - 垃圾邮件异步回调打破了我的 setTimeout 延迟
- c - 在 C 中设置可执行目录中的文件名
- jenkins - 在 jenkins 中运行时,nyc 和 mocha 代码覆盖失败
- prestashop - 覆盖 AdminProductsController
- javascript - 未找到存根路由回调渲染路由状态
- atlassian-sourcetree - 奇怪的 SourceTree 提交错误
- django - Django - 如何将当前经过身份验证的用户与表单提供的数据链接起来