设计模式梳理-单例模式


一概念介绍:
  所谓单例模式,简单来说,就是在整个应用中保证只有一个类的实例存在,而且自行实例化并向整个系统提供这个实例。单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或 文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。
几种写法:
第一种(懒汉,线程不安全):

public class Singleton {
private static Singleton instace;
private Singleton(){}
public static Singleton getInstace(){
if(instace == null){
instace = new Singleton();
}
return instace;
}
}

这是最简单的实现,把类的构造函数写成private的,从而保证别的类不能实例化此类,然后在类中提供一个静态的实例并能够返回给使用者。这样,使用者就可以通过这个引用使用到这个类的实例了。
缺点:这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。
第二种(懒汉,线程安全):

public class Singleton {
private static Singleton instace;
private Singleton(){}
public static synchronized Singleton getInstace(){
if(instace == null){
instace = new Singleton();
}
return instace;
}
}

解决的方法也很简单,那就是加锁,一个线程必须等待另外一个线程创建完成后才能使用这个方法,这就保证了单例的唯一性。
缺点:效率很低,多数情况下不需要同步。
第三种(饿汉):

public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}

或者

public class Singleton {
private static Singleton instace;
static{
instace = new Singleton();
}
private Singleton(){}
public static Singleton getInstace(){
return instace;
}
}

优缺点:
这种方式基于 classloder 机制避免了多线程的同步问题,不过, instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法,   但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
第四种(静态内部类):

public class Singleton {
private static class SingletonClassInstance{
private static final Singleton instace = new Singleton();
}
private Singleton(){}
public static final Singleton getInstace(){
return SingletonClassInstance.instace;
}
}

这种方式同样利用了 classloder 的机制来保证初始化 instance 时只有一个线程,它跟第三种比区别如下:
第三种: 只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果)。
第四种:Singleton类被装载了,instance不一定被初始化,因为:SingletonClass没有static的属性,类没有被主动使用,并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。
场景: 如果实例化 instance 很消耗资源,我想让他延迟加载,另外一方面,我不希望在 Singleton 类加载时就实例化。就比第三种合适了。
第五种(双重校验锁):
   对于第二种加锁方 式的优化。第二种是整个方法都必须加锁,为了优化性能,把检测null的操作和创建对象的操作分离。

public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstace(){
if(instance == null)
{
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}

关于 volatile ,是因为如下原因:
创建一个变量需要哪些步骤呢?一个是申请一块内存,调用构造方法进行初始化操作,另一个是分配一个指针指向这块内存。JVM并没有规定顺序,这两个操作因为编译器优化可能调用构造方法被放在后面。所以用volatile来保证执行顺序。
************总结*******************
实际还是推荐第四种,静态内部类来实现单例模式。也跟我们之前笔记梳理的static串起来了。
相关知识有延迟加载、锁同步及优化,静态内部类等。
当然设计模式的23种很多,我还在学习中,要做到活学活用还要不断尝试,不被规则束缚。

参考:
http://cantellow.iteye.com/blog/838473
http://devbean.blog.51cto.com/448512/203501/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值