创建型模式——单例模式

单例模式简介

单例模式是java创建型模式之一,主要作用是创建唯一对象。


单例模式特点:
1.单例类只有一个实例。
2.单例类必须自己创建自己的唯一实例,即私有化构造方法。
3.单例类必须给其他对象提供这一唯一实例。

单例常见实现

饿汉式

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

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}

饿汉式单例模式在单例类加载的时候就创建了单例对象,由于类加载有JVM控制执行,其过程是线程安全的,所以饿汉式是线程安全的。
特点:
1.线程安全
2.空间换取时间

懒汉式

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式单例模式在单例类加载的时候并没有实例化单例对象,而是在获取单例类唯一对象的方法中,先判断单例对象是否为空,为空的时候实例化单例对象,然后将该对象返回给其他对象。由于第一次获取时单例对象必定为空,所以第一获取时会有实例化对象的过程,执行速度会比之后获取时长。
特点:
1.线程不安全(后面分析)
2.时间换取空间

线程安全下的单例模式

为什么懒汉式不是线程安全的?

如果有多个线程同时第一次调用getInstance方法,在第一个线程判断instance为空进入if语句块准备执行 new Singleton()时,第二个线程也进入了getInstance方法,由于第一个线程还未执行完new Singleton()方法,此时instance对象为空,从而使第二个线程也进入了if语句块。同样的情况可能发生在N个线程中,从而instance可能被初始化N次。这样就失去了单例的唯一性。

synchronized同步getInstance实现线程安全。

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
    
//    public static Singleton getInstance() {
//        synchronized (Singleton.class) {
//            if (instance == null) {
//                instance = new Singleton();
//            }
//        }
//        return instance;
//    }
}

将getInstance整个方法用sychronized关键字修饰,这样在多线程访问getInstance的时候同时进入方法进行判断的只有一个线程,这样就可以避免多次实例化单例对象。但是这样锁粒度太大了,导致多线程获取实例化对象的效率大大降低。(注释部分同样能保证线程安全,但同样所锁密度太大)

DCL(Double Check Lock)

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

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

//    public static Singleton getInstance(){
//        if (instance == null)
              //多个线程进入此处,虽然后面的线程会等待锁施放,
              //但是同时也能进入同步代码块,导致实例化多个单例对象,从而线程不安全。
//            synchronized (Singleton.class) {
//                instance = new Singleton();
//            }
//        }
//        return instance;
//    }
}

分析注释部分代码,最后出现了DCL双重锁单例模式。多个线程虽然可以进入第一个instance==null的if语句块,但是由于后续的同步代码块中又判断了一次instance == null,所以并不会实例化多个单例对象。从而保证了线程安全。

DCL的隐患

DCL看似完美,但是依然存在隐患,而这个隐患就在instance = new Singleton()这个句代码上。

在代码看来只有一句,但是jvm在执行这句语句的时候会有3个步骤
1.在java堆上分配一块内存M
2.在M上执行实例化单例对象
3.将M地址指向instance

以上顺序是我们需要的顺序,然而JVM在执行的时候会进行指令重排和优化,优化后的执行顺序会成为:
1.在java堆上分配一块内存M
2.将M地址指向instance
3.在M上实例化单例对象

在多线程中,如果jvm按照优化过后的顺序执行到2的时候,其他线程调用了getInstance()方法,此时在第一个判断instance == null时会返回false,从而绕过同步代码块,直接返回instance对象引用,然而此时instance对象并没有实例化完成,从而在其他线程调用的instance时发生NPE。
解决方法:此处的问题涉及到java并发编程中的有序性,使用volatile关键字修饰instance即可。

java并发编程之原子性,可见性,有序性(还没写^_^)

其他单例模式实现

1.静态内部类实现单例

public class Singleton {

    private Singleton() {

    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

特点:延迟加载,线程安全

2.枚举实现单例

public enum EnumSingleton {
    /**
     * 单例对象
     */
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

特点:防止反射创建多个单例对象

3.使用容器实现单例

public class SingletonManager {
    private static Map<String, Object> singletonMap = new HashMap<>();

    private SingletonManager() {
    }

    public static void registerService(String key, Object singleton) {
        if (singletonMap.containsKey(key)) {
            throw new RuntimeException("重复注册");
        } else {
            singletonMap.put(key, singleton);
        }
    }

    public static Object getService(String key) {
        if (singletonMap.containsKey(key)) {
            return singletonMap.get(key);
        } else {
            return null;
        }
    }
}

特点:方便管理,android中的服务使用此方式。

Kotlin单例object实现

在使用Kotlin语言时,实现单例是非常简单的

object Singleton{

}

通过kotlinc将Singleton的kt文件编译成class文件:

public final class Singleton{
    public static final SingletonINSTANCE;

    private Singleton() {
    }

    static {
        Singleton var0 = new Singleton();
        INSTANCE = var0;
    }
}

koltin中的object单例使用的是饿汉式实现的,所以是线程安全的。

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

推荐阅读更多精彩内容