设计模式一:单例模式

版权声明,转载请著名出处:http://www.jianshu.com/p/fb3e4ea5be06

设计模式系列

1. 设计模式一:单例模式

2. 设计模式二:观察者模式(发布订阅模式)

一.单例模式的诞生背景

在一个项目中我们需要控制类的实例只能有一个,而且客户端只能从一个全局访问点访问到它就可以使用单例模式,单例模式的本质就是控制实例的数目(在这里就是一个)。单例模式分为懒汉模式和饿汉模式。

二.单例模式的使用场景

1.要求生成唯一的序列号的环境
2.在整个项目中需要有访问一个共享访问点或共享数据,如配置文件;
3.创建一个对象需要消耗的资源过多,如数据库的访问;
4.需要定义大量的静态常量或静态方法(如工具类)的环境。

三.单例模式的代码示例

饿汉模式

public class Singleton {
    // 2.创建静态实例
    private static final Singleton instance = new Singleton();
    // 1.私有构造方法
    private Singleton() {
    }
    // 3.提供静态的对象提供方法
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉模式初级版

public class Singleton {
    // 2.创建静态变量
    private static Singleton instance;
    // 1.私有构造方法
    private Singleton() {
    }
    // 3.提供静态的对象提供方法,先检查对象是否实例化,若为null则实例化
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

饿汉模式自带线程安全的属性,但是懒汉模式上面的这种写法,是线程不安全的,当多线程环境中有可能创建多个实例,因此我们可以对getInstance( )方法进行加锁,出现懒汉模式升级版一。

懒汉模式升级版一

    public synchronized static Singleton getInstance() {     //性能低
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

升级版一虽然解决了线程安全问题,但是由于每次获取对象实例时都需要同步,这样性能会明显下降,其实我们可以增加判断,当对象为null时,才需要同步进行创建对象,如果不为null则可以直接返回,出现懒汉模式升级版二。

懒汉模式升级版二(双重检验加锁)

当获取实例时先判断对象是否实例化,若存在直接返回,若不存在进行同步操作,同步操作再次判断对象是否为null,是则实例化对象,代码入下:

public class Singleton {         //适用JDK1.5及以后
    private volatile static Singleton instance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();//由于不是原子操作,禁止指令重排序优化,因此instance需要 volatile关键字修饰
                }
            }
        }
        return instance;
    }
}

双重检验加锁机制分析

这段代码看起来很完美,很可惜,它是有问题。主要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。
1.给 instance 分配内存
2.调用 Singleton 的构造函数来初始化成员变量
3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。我们只需要将 instance 变量声明成 volatile 就可以了,volatile禁止指令重排序优化。但是特别注意在 Java 5 以前的版本使用了 volatile 的双检锁还是有问题的。其原因是 Java 5 以前的 JMM (Java 内存模型)是存在缺陷的,即时将变量声明成 volatile 也不能完全避免重排序,主要是 volatile 变量前后的代码仍然存在重排序问题。这个 volatile 屏蔽重排序的问题在 Java 5 中才得以修复,所以在这之后才可以放心使用 volatile。

懒汉模式升级版三(静态内部类)

这里采用静态初始化的方式,它由JVM来保证线程安全性,当类装载的时候不去初始化对象,在这个内部类中去实例化对象,因此只要不使用内部类就不会创建对象,这样能解决线程安全的问题还可以实现延迟加载且不会增加任何访问开销,代码如下:

public class Singleton {        //推荐,但只能在无参构造的时候使用
    private  Singleton (){}
    private static  class SigletonHolder{
        private  static  Singleton instance=new Singleton();
    }
    public  static  Singleton getInstance(){
        return SigletonHolder.instance;
    }
}

四.单例模式的总结(优缺点,比较)

比较:

饿汉模式:类加载速度比较满,获取对象使用时比较快,适用无参构造的,线程安全的;
懒汉模式:类加载速度比较快,第一次获取对象使用时比较慢,线程不安全的(可以通过加锁解决),体现了延迟加载的思想,

解决懒汉模式的线程安全问题:

1、由于懒汉是线程不安全的,因此可以采用双重检查加锁机制,即进入获取实例的方法后先检查实例是否存在,若不存在进入同步代码块,再次检查实例是否存在,如果不存在才真正创建实例,但是需要属性定义时需要volatile 关键字修饰(适用JDK1.5及以后)
2、对getInstance方法进行加锁(性能底);
3、静态内部类的静态属性;

如何选择

如果单件模式实例在系统中经常会被用到,饿汉式是一个不错的选择。反之如果单件模式在系统中会很少用到或者几乎不会用到,那么懒汉式是一个不错的选择。

单例模式的本质

控制实例的数目,当需要控制类的实例只能有一个,而且客户端只能从一个全局访问点访问到它就可以使用单例模式。

局限性

只能保证在一个JVM中保持单例,高并发的服务器中需要其他的控制来实现;

相关知识或概念

延迟加载(lazy  load): 一开始不要加载资源或者数据,一直等到马上要使用的这个资源或者数据时,实在躲不过去了才去加载;
缓存的思想: 每次操作的时候,先到内存中找,看有没有这些数据,如果有那么直接使用,如果没有那么就去获取他并设置到缓存中,下次访问的时候就可以直接从内存中读取了,从而节省大量的时间,是一种典型以空间换时间的方案

参考链接

http://www.cnblogs.com/dolphin0520/p/3920373.html
http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/

我的CSDN博客地址:http://blog.csdn.net/wo_ha/article/details/77567446

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容