第一种(懒汉,线程不安全):
-
public
class
Singleton {
-
private
static
Singleton instance;
-
-
public
static
Singleton getInstance() {
-
if
(instance ==
null
) {
-
instance = new
Singleton();
-
}
-
return
instance;
-
}
-
}
这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。
第二种(懒汉,线程安全):
-
public
class
Singleton {
-
private
static
Singleton instance;
-
-
public
static
synchronized
Singleton getInstance() {
-
if
(instance ==
null
) {
-
instance = new
Singleton();
-
}
-
return
instance;
-
}
-
}
这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。
第三种(饿汉):
-
public
class
Singleton {
-
private
static
Singleton instance =
new
Singleton();
-
-
public
static
Singleton getInstance() {
-
return
instance;
-
}
-
}
这种方式基于
classloder
机制避免了多线程的同步问题,不过,
instance
在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用
getInstance
方法,
但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化
instance
显然没有达到
lazy loading
的效果。
第四种(饿
汉,变种):
-
public
class
Singleton {
-
private
Singleton instance =
null
;
-
static
{
-
instance = new
Singleton();
-
}
-
-
public
static
Singleton getInstance() {
-
return
this
.instance;
-
}
-
}
表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。
第五种(静态内部类):
-
public
class
Singleton {
-
private
static
class
SingletonHolder {
-
private
static
final
Singleton INSTANCE =
new
Singleton();
-
}
-
-
public
static
final
Singleton getInstance() {
-
return
SingletonHolder.INSTANCE;
-
}
-
}
这种方式同样利用了
classloder
的机制来保证初始化
instance
时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要
Singleton
类被装载了,那么
instance
就会被实例化(没有达到
lazy loading
效果),而这种方式是
Singleton
类被装载了,
instance
不一定被初始化。因为
SingletonHolder
类没有被主动使用,只有显示通过调用
getInstance
方法时,才会显示装载
SingletonHolder
类,从而实例化
instance
。想象一下,如果实例化
instance
很消耗资源,我想让他延迟加载,另外一方面,我不希望在
Singleton
类加载时就实例化,因为我不能确保
Singleton
类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化
instance
显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
第六种(枚举):
-
public
enum
Singleton {
-
INSTANCE;
-
public
void
whateverMethod() {
-
}
-
}
这种方式是Effective Java作者Josh Bloch
提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特
性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。
第七种(双重校验锁):
-
public
class
Singleton {
-
private
volatile
static
Singleton singleton;
-
-
public
static
Singleton getSingleton() {
-
if
(singleton ==
null
) {
-
synchronized
(Singleton.
class
) {
-
if
(singleton ==
null
) {
-
singleton = new
Singleton();
-
}
-
}
-
}
-
return
singleton;
-
}
-
}
这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5之后,双重检查锁定才能够正常达到单例效果。
总结
有两个问题需要注意:
1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet
容器对每个servlet
使用完全不同的类装载器,这样的话如果有两个servlet
访问一个单例类,它们就都会有各自的实例。
2.如果Singleton
实现了java.io.Serializable
接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的办法是:
-
private
static
Class getClass(String classname)
-
throws
ClassNotFoundException {
-
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-
-
if
(classLoader ==
null
)
-
classLoader = Singleton.class
.getClassLoader();
-
-
return
(classLoader.loadClass(classname));
-
}
-
}
对第二个问题修复的办法是:
-
public
class
Singleton
implements
java.io.Serializable {
-
public
static
Singleton INSTANCE =
new
Singleton();
-
-
protected
Singleton() {
-
-
}
-
private
Object readResolve() {
-
return
INSTANCE;
-
}
-
}
对我来说,我比较喜欢第三种和第五种方式,简单易懂,而且在JVM层实现了线程安全(如果不是多个类加载器环境),一般的情况下,我会使用第三种方
式,只有在要明确实现lazy
loading效果时才会使用第五种方式,另外,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证我的程序是线程安全
的,而且我永远不会使用第一种和第二种方式,如果有其他特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。
========================================================================
superheizai
同学总结的很到位:
不过一般来说,第一种不算单例,第四种和第三种就是一种,如果算的话,第五种也可以分开写了。所以说,一般单例都是五种写法。懒汉,恶汉,双重校验锁,枚举和静态内部类。
我很高兴有这样的读者,一起共勉。
鉴于很多人说我没有写私有构造方法,我这里声明一下,不是我忘了写,是我故意嫌麻烦省略掉了,我以为大家都懂得
分享到:
相关推荐
3. 单例模式的7种实现方式 5 3.1饿汉式(使用静态常量) 5 3.2饿汉式(使用静态代码块) 7 3.3懒汉式(线程不安全) 8 3.4懒汉式(线程安全,使用同步方法) 9 3.5双重检查实现单例模式 10 3.6使用静态内部类实现...
此文档为Tom老师的公开课的单例的7种写法的一个文档,充分分析单例模式,值得对设计模式有研究的童鞋下下来好好看看
单例模式的七种写法: 1.第一种(懒汉,线程不安全) 2.第二种(懒汉,线程安全) 3.第三种(饿汉) .....
主要给大家介绍了关于Java版的7种单例模式写法,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
关于单例模式的知识要点: 1、某个类只能有一个实例 2、它必须自行创建这个实例 3、必须自行向这个系统提供这个实例
java单例模式开发的7中写法,网上搜索的,可以看看
本压缩包含有两个demo,分别对应<设计模式>第2版,刘伟这本书,单例模式这一章课后习题6、7,如果问题请留言一起探讨,谢谢!
NULL 博文链接:https://zhaohong.iteye.com/blog/483396
这种写法可以保证线程安全.两个if都是不能去掉的.如果去掉第一个if: 那么所有的线程都会到这里来先获取锁,然后判断singleton是否为空.所有线程都会串行
单例模式的实现饿汉式懒汉式线程安全的懒汉式登记式单例模式的优缺点单例模式的优点单例模式的缺点 单例模式是23个模式中比较简单的模式,应用也非常广泛 什么是单例模式? 单例模式的定义: Ensure a class ...
设计模式之——单例模式单例的几种实现1. 懒汉单例模式2. synchronized 修饰的懒汉单例模式3. 双重检查锁定的单例模式4. 静态内部类实现单例模式5. 饿汉实现单例模式6. 饿汉变种实现单例模式7. 枚举实现单例模式...
单例模式是一种设计模式,它的具体实现和各种语言特性有关,这里主要介绍在C++上面的实现,测试平台为Win7 64位,VS2010开发环境。 根据参考博文中的例子,在此先列举一下各种实现策略,以下均以CSingleton为...
最后我就封装了一个单例模式的数据库类 使用单例模式是为了避免生成多个实例,浪费资源 下面是封装的代码 class Mongo_db { private static $cli; /** * 不允许初始化 */ private function __construct() { ...
1. 写法一(只适用于单线程环境) public sealed class Singleton1 { private Singleton1(){} private static Singleton1 instance = null; public static Singleton1 Instance { get ...
创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。 行为型模式(11种):策略...
2 单例模式 2 门面模式 2 模板方法模式 3 策略模式 3 工厂方法模式 3 组合模式 4 抽象工厂模式 4 代理模式 4 命令模式 5 迭代器模式 5 适配器模式 5 原型模式 6 简单工厂模式 6 解释器模式 6 享元模式 7 桥接模式 7 ...
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:...
其实单例模式在C#或者.NET里面更好理解,像win7的任务管理器,在系统中只能创建一个。有些理解了嘛? 单例模式只能有一个实例,实例化其实就是new的过程,是不可能阻止他人不去用new的。所以我们完全可以直接就把这...