在Java中实现单例设计模式的有效方法是什么?
当前回答
我见过的最好的单例模式使用Supplier接口。
它是通用的,可重复使用它支持延迟初始化它只有在初始化之前才同步,然后将阻塞供应商替换为非阻塞供应商。
见下文:
public class Singleton<T> implements Supplier<T> {
private boolean initialized;
private Supplier<T> singletonSupplier;
public Singleton(T singletonValue) {
this.singletonSupplier = () -> singletonValue;
}
public Singleton(Supplier<T> supplier) {
this.singletonSupplier = () -> {
// The initial supplier is temporary; it will be replaced after initialization
synchronized (supplier) {
if (!initialized) {
T singletonValue = supplier.get();
// Now that the singleton value has been initialized,
// replace the blocking supplier with a non-blocking supplier
singletonSupplier = () -> singletonValue;
initialized = true;
}
return singletonSupplier.get();
}
};
}
@Override
public T get() {
return singletonSupplier.get();
}
}
其他回答
有时简单的“static Foo=new Foo();”是不够的。想想你想做的一些基本数据插入。
另一方面,您必须同步任何实例化单例变量的方法。同步本身并不坏,但它可能会导致性能问题或锁定(在非常罕见的情况下,使用本示例。解决方案是
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
// do some of your instantiation stuff here
}
private Singleton() {
if(instance!=null) {
throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
}
}
public static getSingleton() {
return instance;
}
}
现在会发生什么?类通过类加载器加载。在从字节数组解释类之后,VM立即执行静态{}-块。这就是全部秘密:静态块只被调用一次,即这个单类加载器加载给定包的给定类(名称)的时间。
如果您不需要延迟加载,那么只需尝试:
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return Singleton.INSTANCE; }
protected Object clone() {
throw new CloneNotSupportedException();
}
}
如果您希望延迟加载并且希望单例是线程安全的,请尝试双重检查模式:
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if(null == instance) {
synchronized(Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
protected Object clone() {
throw new CloneNotSupportedException();
}
}
由于双重检查模式不能保证有效(由于编译器的一些问题,我对此一无所知),因此您也可以尝试同步整个getInstance方法或为所有单例创建注册表。
我对一些答案感到困惑,这些答案建议依赖注入(DI)作为使用单态的替代方案;这些都是不相关的概念。您可以使用DI注入单例或非单例(例如,每个线程)实例。至少如果您使用Spring2.x,这是正确的,我不能为其他DI框架说话。
所以我对OP的回答是(除了最简单的示例代码之外):
使用类似Spring framework的DI框架,然后将其作为DI配置的一部分,无论依赖项是单体的、请求范围的、会话范围的还是其他。
这种方法为您提供了一个很好的解耦(因此是灵活和可测试的)架构,其中是否使用单例是一个容易可逆的实现细节(当然,前提是您使用的任何单例都是线程安全的)。
我见过的最好的单例模式使用Supplier接口。
它是通用的,可重复使用它支持延迟初始化它只有在初始化之前才同步,然后将阻塞供应商替换为非阻塞供应商。
见下文:
public class Singleton<T> implements Supplier<T> {
private boolean initialized;
private Supplier<T> singletonSupplier;
public Singleton(T singletonValue) {
this.singletonSupplier = () -> singletonValue;
}
public Singleton(Supplier<T> supplier) {
this.singletonSupplier = () -> {
// The initial supplier is temporary; it will be replaced after initialization
synchronized (supplier) {
if (!initialized) {
T singletonValue = supplier.get();
// Now that the singleton value has been initialized,
// replace the blocking supplier with a non-blocking supplier
singletonSupplier = () -> singletonValue;
initialized = true;
}
return singletonSupplier.get();
}
};
}
@Override
public T get() {
return singletonSupplier.get();
}
}
忘记延迟初始化;这太有问题了。这是最简单的解决方案:
public class A {
private static final A INSTANCE = new A();
private A() {}
public static A getInstance() {
return INSTANCE;
}
}
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap