美文网首页
单例模式

单例模式

作者: C调路过 | 来源:发表于2020-03-16 23:50 被阅读0次

前言

单例模式常用在一些全局唯一的管理类,避免对象重复创建,节省内存创建释放开销。

饿汉模式

每一次通过 new 创建的都是一个新的对象,为了保证全局唯一,我们就可以利用static关键字的特性,创建全局且线程共享的对象。这就是单例的第一种实现:饿汉模式

    public class Singleton {

        private static final Singleton instance = new Singleton();

        // 重要:写单例的第一件事就是将默认构造函数私有化,避免外部通过new来创建对象
        private Singleton() {
        }

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

懒汉模式

优秀的程序员懂得优化代码的时间空间,避免资源的浪费。在首次调用单例前,并没有初始化的必要,引出单例的第二种实现:懒汉模式

  1. 先私有化默认构造函数
  2. 在getInstance()方法中判断对象是否存在,没有就先new一个再返回,完美!
    public class Singleton2 {

        private static Singleton2 instance;

        // 重要:写单例的第一件事就是将默认构造函数私有化,避免外部通过new来创建对象
        private Singleton2() {

        }

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

为了模拟正常初始化的运行时间,在制造函数中给1ms的sleep。

        private Singleton2() {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

然后我们在Main函数中疯狂使用。

        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Singleton2.getInstance().toString());
                }
            }).start();

        }

        -----------打印结果---------------
        singleton.Main$Singleton2@6d4bc03f
        singleton.Main$Singleton2@7f5b7c1b
        singleton.Main$Singleton2@b148489
        singleton.Main$Singleton2@30f51e1e
        singleton.Main$Singleton2@42f5503f
        singleton.Main$Singleton2@1bcc4803
        singleton.Main$Singleton2@7bae2ed5
        singleton.Main$Singleton2@701e866
        singleton.Main$Singleton2@4cf319d1
        singleton.Main$Singleton2@60174d9
        singleton.Main$Singleton2@71d9ab02
        singleton.Main$Singleton2@6d4bc03f
        singleton.Main$Singleton2@b20568f
        singleton.Main$Singleton2@71d9ab02
        singleton.Main$Singleton2@71d9ab02
        singleton.Main$Singleton2@71d9ab02
        singleton.Main$Singleton2@6299d4f3
        singleton.Main$Singleton2@9a662fb
        singleton.Main$Singleton2@1b73c3bb
        singleton.Main$Singleton2@3b637176
        singleton.Main$Singleton2@3b637176
        singleton.Main$Singleton2@3b637176
        singleton.Main$Singleton2@3b637176

会发现在多线程并发环境下,不能保证单例的线程安全。在第一次请求创建过程中,另外的线程进行读取,对象instance仍为空,开始了另一个对象的创建。
于是想到了给getInstance方法加锁,保证线程安全

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

但这样造成每次获取实例都要进行加锁耗时,我们想要的只是在第一次创建对象的时候加个锁。解决方式也很简单,于是给getInstance改写成给代码块加锁的方式。

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

也就是所说的双重检查锁(double checked locking)。

然而这并不是我们所见的标准DCL单例的写法,new Singleton2()创建对象并非是一个原子操作(类比 i = i + 1,首先是读取,加一,再赋值,需要多步完成),创建对象需要

  1. 分配内存
  2. 将对象指向内存
  3. 初始化对象

在不同的编译器可能对2,3步骤进行重排序,即先赋值给对象然后才进行初始化,会导致getInstance返回的实例为null的情况。于是instance对象加上volatile修饰,保证对象初始化和赋值操作不进行重排,避免创建了对象但是instance为空的情况。

    private volatile static Singleton2 instance;

静态内部类实现

需要熟悉类加载机制。静态内部类的方式即能保证在使用时创建,也能保证线程安全,但是不能在初始化时传递参数。如我们需要对单例设置如Context时,选择DCL的方式更为合适。

public static class Singleton3 {

    private Singleton3() {
        System.out.print("Singleton3初始化");
    }

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

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

其他

还有通过枚举类来创建单例的,感觉平时不会用到。
涉及知识点 static final synchronized关键字,类加载机制,原子性,重排序。

相关文章

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 单例模式

    单例模式1 单例模式2

  • java的单例模式

    饿汉单例模式 懒汉单例模式

网友评论

      本文标题:单例模式

      本文链接:https://www.haomeiwen.com/subject/rzyaehtx.html