使用静态同步getInstance方法的单例的正确用法是什么?为什么?
例如:
MySingleton mySingleton = MySingleton.getInstance();
.....
mySingleton.doSomething();
其中mySingleton
将是一个字段并在整个班级中使用。
与
MySingleton.getInstance().doSomething();
编辑:正确地说,我的意思是编码风格和线程安全。
答案 0 :(得分:1)
比使用单身人士更好的解决方案是使用dependency injection:
class MyClass {
private final MySingleton mySingleton;
MyClass(MySingleton mySingleton) {
this.mySingleton = mySingleton;
}
// Use mySingleton in instance methods as required.
}
这里的优势在于它与单身人士相关的事实并不相关 - 它可以是一个单身人士,但它并不需要。
我打电话给mySingleton
字段,因为这是您在问题中所称的内容,但不再要求它真正成为单身人士:唯一的就是{ {1}}并不需要关心实例的生命周期。
这会破坏MyClass
和MyClass
之间的静态耦合,如果您通过问题中描述的任何一种方式来实现它。这样可以更轻松地测试MySingleton
,以及其他优势。
答案 1 :(得分:1)
有人必须实例化单身人士。这只需要进行一次。这里有线程安全发挥。如果有可能不同的线程调用
SingletonClass.getInstance();
几乎在同一时间,单身人士的实例化必须得到这样的保护:
private static volatile SingletonClass myInstance;
public static SingletonClass getInstance()
{
if ( myInstance == null )
{
synchronized( SingletonClass.class)
{
if ( myInstance == null )
{
myInstance = new SingletonClass();
}
}
}
return myInstance;
}
这称为"双重检查锁定"。变量必须是易变的。
如果SingletonClass的方法在线程之间共享状态(例如,您有一个由一个线程编写并由另一个线程读取的成员),则必须特别注意同步。但这很常见,与Singleton模式无关。
您的问题" XY.getInstance()。doSomething()"之间的区别是什么?与保留自己成员中的引用以便以后访问它:它只是一个(次要的)性能损失(callstack还有一个,if语句,返回)。但IMO这是可以忽略的,因为从长远来看,java优化器无论如何都会内联这个。另一方面:保持引用可以使代码更短,更易读,特别是如果您经常访问单例。最后,这是一个品味问题。
答案 2 :(得分:1)
虽然我同意Andy Turner在可能的情况下避免单身人士的答案,但我会将其添加为完整性:静态同步的getInstance方法是过去的遗物。至少有3个单例模式实现不需要。
a)渴望单身人士
public class EagerSingleton{
private EagerSingleton(){}
private static final EagerSingleton INSTANCE = new EagerSingleton();
public static EagerSingleton getInstance(){ return INSTANCE; }
}
无论何时以任何方式引用类EagerSingleton(例如,通过声明该类型的变量),都会在类加载时初始化。
b)懒惰的单身人士
public class LazySingleton{
private LazySingleton(){}
private static class Holder{
private static final LazySingleton INSTANCE = new LazySingleton();
}
public static LazySingleton getInstance(){ return Holder.INSTANCE; }
}
这是在第一次调用方法getInstance()时在调用时初始化的。类加载器现在加载holder类并初始化INSTANCE字段,并保证以同步方式执行此操作。如果您的单身人士设置昂贵,请使用此版本,但并非总是如此。
c)enum Singleton
public enum EnumSingleton{
INSTANCE;
// and if you want, you can add the method, too, but it's
// unnecessary:
public static EnumSingleton getInstance(){ return INSTANCE; }
}
枚举项是编译时常量,即它们的唯一性在编译时得到保证,不仅在运行时(在运行时,单例对于每个类加载器都是唯一的,并且适用于所有版本)。这是最好的方法,除非您的要求是从现有的课程扩展。
enum版本免费提供许多其他功能:
因此,下次当您看到某人编写双重检查锁定机制或类似情况时,请告诉他们这些机制过于复杂和技术性。让类加载器为您完成工作。
使用所有3个版本:确保您使用的功能由接口支持,并针对该接口提供代码,而不是实现类型。这将使您的代码可测试。
答案 3 :(得分:0)
我认为最好为班级中的每个单身人士定义一个字段。因此,只需查看前n行,就可以轻松地找到系统中单元的依赖关系。它有助于理解一个类的耦合。
此外,为单例定义字段使测试更容易。如果您有可能将单例的方法移动到接口,则可以为单例定义存根类,并且可以在测试代码中注入存根单例。
对于线程安全部分,如果你的单例是线程安全的,那么应该没有任何问题。