java设计模式-单例模式

单例模式在我们日常开发中算是使用频率最高的设计模式了,为什么单例模式的使用频率会这么高呢?

有时我们的对象会占用一些系统资源,所以我们需要保证这些对象在整个系统中只有一个实例对象。甚至在我们自己设计类的时候,如果这个类的对象存在多个的时候可能会对我们的功能造成一定影响,我们都可以将其设计成一个单例的形式。就好比一个公司在一个时间只会存在一个董事长、一个国家最会存在一个总统或者主席是一样的道理。

单例类的特点就是只保证只会存在一个实例对象、它的构造方法必须私有化、它的对象由自己或者它的外部类来进行创建。这里我们主要说下饿汉式、懒汉式、双重检查锁形式、枚举形式、内部类形式、容器形式

1、饿汉式

public class Singleton {

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

    }

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

这种方式在类创建的时候就会将实例对象创建出来,所以也是线程安全的,但是它也达不到我们的lazy loading的要求。

2、懒汉式

public class Singleton {
    private static volatile Singleton instance;

    private Singleton(){

    }

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

这种方式是在类加载的时候并不会去创建它的实例对象,而是在外部调用getInstance方法的时候创建,考虑到多线程问题,所以将方法同步。但是这样就会造成每次调用该方法的时候都会进行同步,造成资源的浪费。

3、DoubleCheckLock(DCL)

public class Singleton {
    private static volatile Singleton instance;

    private Singleton(){

    }

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

考虑到懒汉式每次都会进行同步,所以这种DCL的方式只会在创建对象的时候进行同步,以后再调用的时候将不会进行同步了。因为加入的双重判断又有同步情况,所以在第一次创建的时候效率有点低。在大多数情况下这种方式能够保证对象唯一。

这里加上volatile的原因是为了在初始化单例对象的时候防止线程切换后使用单例对象出错。

在JVM处理instance = new Singleton()的时候有3个步骤,而且这个步骤顺序是不确定的。

①为Singleton对象开辟内存

②调用Singleton的构造函数初始化对象

③将instance指向分配的内存空间

如果线程A执行顺序是①③②,当执行到③的时候就将线程切换到其他线程B,在B线程中因为单例对象不为null(指向了开辟的内存),所以使用的时候就会出错,对象还没有初始化完成。

注意:在高并发的情况下,DCL这种方式还是有失效的情况。

4、枚举单例

public enum Singleton {
    INSTANCE;
    private Singleton() {

    }

    public void go() {
        System.out.print("ENUM 单例");
    }
}

枚举在初始化创建对象的时候是默认线程安全的,同时对象也是唯一的,它的写法也比较简单。它有一个优势就是在反序列化的时候对象同样是唯一的,但是其他几种方式的单例如果在反序列化的时候则对象不是唯一的。当然也有解决办法就是在类中加入readResolve方法,该方法返回单例对象即可。例如:

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

    }

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

    private Object readResolve() throws ObjectStreamException {
        return SingletonHolder.instance;
    }
}

5、静态内部类单例

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

    }

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

它和饿汉式有点类似,但不同之处在于前者在加载类的时候就会初始化单例对象,而静态内部类方式只有在调用getInstance方法在加载内部类的时候才会初始化,而在jvm加载类的时候又保证了线程安全,所以这种既安全又延迟加载的方式是比较推荐使用的。

6、容器单例模式

容器类单例模式会使用一个map集合在装载单例对象。这种方式可以管理多个单例对象,但是实际上它还是需要保证它的各个单例对象的类不能被外部直接创建。

public class SingletonContainer {
    private static final Map<String,Object> C = new HashMap<>();
    private SingletonContainer() {
        throw new RuntimeException("");
    }

    public static void putObject(String key,Object o) {
        if (!C.containsKey(key)) {
            C.put(key,o);
        }
    }

    public static Object getObject(String key) {
        if (C.containsKey(key)) {
            return C.get(key);
        }
        return null;
    }
}

这种方式很少用到,不过在Android中的Context里面使用到了这种方式。在android中获取系统服务调用getSystemService这个方法的时候就是使用的map形式获取。它的单例对象管理类是SystemServiceRegistry,它被加载时就会初始化很多系统服务相关的类对象。在ContextImpl这个类中有这么一行代码,它是Context的子实现类:

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

推荐阅读更多精彩内容