SyncApi
类的一个实例访问我的API。 > public class Api {
private static SyncApi api = null;
static {
synchronized (api) {
if (api == null) {
api = new ApiFActory().getSyncApi();
}
}
}
public static SyncApi getInstance() {
return api;
}
}
#1 楼
比尔·普格(Bill Pugh)在Java中有一个建立线程安全单例的惯用语:进行显式同步和检查。这样做的好处是消除了微妙的比赛条件和多余的锁定的可能性。
评论
\ $ \ begingroup \ $
SyncApi类是如何定义的?我相信,如果Api和/或SyncApi不是最终版本,则此实现可能会被破坏。可以扩展这些类并以完全不受此代码控制的方式创建对象。 SyncApi的子类的每个实例显然也将是SyncApi的一个实例。基于枚举的解决方案不受此影响,因为所有枚举都是隐式最终的。
\ $ \ endgroup \ $
– toniedzwiedz
13年11月16日在20:51
\ $ \ begingroup \ $
@Tom实际上,我只是在某种程度上复制并粘贴了OP的代码,实际上我是说这两个类是一个类(即SyncApi = Api),所以这段代码是安全的(无法扩展Api)。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
13年11月18日在8:39
\ $ \ begingroup \ $
好吧,我想念私有构造函数是唯一的那个。最终不会为清晰起见。
\ $ \ endgroup \ $
– toniedzwiedz
13年11月18日在12:16
\ $ \ begingroup \ $
不要使用此代码,请使用aivikiss用户在答案中给出的基于枚举的安全单例成语。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
19年7月31日在12:28
#2 楼
最好,最简单的方法是Joshua Bloch在《有效的Java第2版》中给出的。br />
评论
\ $ \ begingroup \ $
这根本不是单例。
\ $ \ endgroup \ $
– mjcopple
2011年4月7日15:32
\ $ \ begingroup \ $
是的,很抱歉。枚举不同于单例。
\ $ \ endgroup \ $
– jjnguy
2011年4月7日15:43
\ $ \ begingroup \ $
为什么?枚举是类,不是吗?如果在上面的Api类上定义其他非静态方法,则它的行为将与单例完全相同,因为它是单例。
\ $ \ endgroup \ $
–romacafe
2011年4月7日在18:46
\ $ \ begingroup \ $
@rom,很好。我想看看这个例子。
\ $ \ endgroup \ $
– jjnguy
2011年4月9日在2:21
#3 楼
好吧,这绝对不是Singleton,因为此代码是合法的:Api.api=new Api();
您至少应将api变量定义为final,至少应将private构造函数定义为避免实例化。
请参阅@Konrad Rudolph的解决方案。
#4 楼
如果您想要线程安全和懒惰,我完全同意Konrad Rudolph提供的答案。这是一篇很好的文章,包含许多测试和示例:http://www.javaworld.com/javaworld/jw-04-2003/jw-0425 -designpatterns.html
但是两种方法都不能给您绝对的单例,因为不同的ClassLoader将创建不同的实例。这可能看起来几乎是不可能的情况,但是根据我的经验,我遇到了一个由这种情况引起的错误。 /communication-across-classloaders.xml
评论
\ $ \ begingroup \ $
是-他必须使用枚举来拥有绝对单例
\ $ \ endgroup \ $
– Mr_and_Mrs_D
13年7月21日在12:40
#5 楼
将api设置为null并不能帮助您发现NullPointerExceptionsynchronized (api) // api == null, you should use Api.class
关于单例,请使用枚举。从Java 5开始,这是推荐的模式。现在,如果要延迟加载:
public class Api {
private Api() {}
private enum SingletonHolder {
INSTANCE;
private static final Api singleton = new Api();
Api getSingleton() {
return singleton;
}
}
public Api api() {
Api.SingletonHolder.INSTANCE.getSingleton();
}
}
调用:
api().logOn("jjnguy", "pa55w0rd");
您可以丢弃
getSingleton()
并直接在singleton
内部访问Api
public class Api {
private Api() {/*your costly init here*/}
private enum SingletonHolder {
INSTANCE;
private static final Api singleton = new Api();
}
public Api api() {
return Api.SingletonHolder.singleton;
}
}
评论
\ $ \ begingroup \ $
您无需在枚举类声明中使用static和final关键字,因为enum类已经提供了static和final。
\ $ \ endgroup \ $
–Enes Unal
2014年10月1日12:46
\ $ \ begingroup \ $
@EnesUnal:正确(另请参见stackoverflow.com/a/253282/281545),已编辑
\ $ \ endgroup \ $
– Mr_and_Mrs_D
2014年10月1日下午13:31
#6 楼
我同意所有其他答案,但是正如您所说的,您的问题不是只有一个API实例,而是只有一个SyncApi实例。您将无法阻止用户正在执行:
SyncApi api = new ApiFActory().getSyncApi();
您必须将SyncApi设置为Singleton或将其用法封装在Api类中,并且不能在您的Api之外访问它。
#7 楼
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance(){
if(null == instance){
synchronized(Singleton.class){
//double check
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
可用于确保仅创建单个实例。或只使用Enums。
评论
\ $ \ begingroup \ $
if(null == instance)Yoda风格,这听起来像。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年1月20日,3:40
评论
如果您不想懒惰,为什么不直接在声明中初始化SyncApi实例呢?不需要静态构造函数。 Java保证静态成员的初始化只会发生一次。@Konrad,啊!那么那会有所帮助。我担心初始化(在罕见的多线程情况下)可能会发生多次。
我想指出的是,由于测试困难,单例现在被认为是反模式。另外,您还没有将构造函数设为私有,因此这实际上不是Singleton。
这里的同步可能无法正常工作,所有这些都在Joshua Bloch的Effective Java中进行了解释,或者在Wikipedia中提供了一些示例
@Athas Singletons被认为是一种反模式,因为它采用了封装和数据流控制这一不错的概念,并表示“嘿,让我们现在回到拥有全局变量的地方。”。