美文网首页程序员
单例模式的实现

单例模式的实现

作者: 晚安多巴胺 | 来源:发表于2019-03-05 14:14 被阅读0次

    两种模式

    • 饿汉模式

    饿汉模式:加载类时比较慢,运行时获取对象的速度快,线程安全

    • 懒汉模式

    懒汉模式:加载类时比较快,运行时获取对象的速度慢,线程不安全

    实现思路

    单例模式要求类能够返回对象一个引用(永远是同一个)和一个获取该实例的方法(必须是静态方法)

    实现步骤

    1.将构造方法私有化,不允许外界直接创建对象
    2.声明唯一的实例,使用private static修饰
    3.提供一个public static 修饰的获取实例的方法

    注意事项

    懒汉模式是线程不安全的,当唯一实例未创建时,同时有两个线程调用创建方法,又同时检测到没有唯一实例,而各自创建了一个实例,这时就有两个实例构造出来了,违反了单例模式唯一的原则。让我们接着往下看,有什么解决方法

    静态模式的八种写法

    1.饿汉模式(静态常量)【可用】

    public class Singleton{
      // 将构造方法私有化,不允许外界直接创建对象
      private Singleton(){}
     // 声明唯一实例
      private final static Singleton instance = new Singleton();
      // 提供一个获取实例的方法
      public static Singleton getInstance(){
        return instance;
      }
    }
    
    优点:写法简单,在类加载的时候就完成实例化,避免线程同步问题
    缺点:在类加载的时候完成实例的创建,没有达到Lazy Loading的效果。如果没有用过这个实例,会造成内存浪费

    2.饿汉模式(静态代码块)【可用】

    public class Singeton{
        private static Singleton instance;
        static{
          instance = new Singleton();
        }
        private Singleton(){}
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    优点:同上,不过将类实例化的过程放在静态代码块中,在类加载的时候,执行静态代码块里面的代码,初始化类实例
    缺点:同上

    3.懒汉模式(线程不安全)【不可用】

    public class Singeton{
        private static Singleton instance;
        private Singleton(){}
        public static Singleton getInstance() {
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
    优点:起到Lazy Loading效果
    缺点:只能在单线程时使用,可能同时两个线程进入判读语句,产生多个实例

    4.懒汉模式(线程安全,同步方法)【不可用】

    public class Singeton{
        private static Singleton instance;
        private Singleton(){}
        public static synchronized Singleton getInstance() {
            if(singleton == null){
                singleton = new Singleton();
            }
            return instance;
        }
    }
    
    优点:解决了上面线程不安全的问题,对getInstance()方法进行了线程同步
    缺点:效率太低,每个线程想获取类的实例时,执行getInstance()方法都要进行同步,其实只有第一次对象未实例化时需要同步,以后直接return就可以了

    5.懒汉模式(线程安全,同步代码块)【不可用】

    public class Singeton{
        private static Singleton instance;
        private Singleton(){}
        public static  Singleton getInstance() {
            if(singleton == null){
                synchronized (Singleton.class){
                  singleton = new Singleton();
                }
            }
            return instance;
        }
    }
    
    改进了上一个方法,采用同步代码块,但是并不能起到线程同步的作用,如果同时两个线程进入判读,虽然实例化的部分加上了锁,同时只有一个线程可以实例化,但是待第一个线程释放了锁,第二个线程也会实例化一次(已经进入了判读),产生多个实例

    6.懒汉模式(双重检查 )【推荐用】

    public class Singeton{
        private static volatile Singleton instance;
        private Singleton(){}
        public static  Singleton getInstance() {
            if(singleton == null){
                synchronized (Singleton.class){
                  if(singleton == null){
                      singleton = new Singleton();
                  }
                }
            }
            return instance;
        }
    }
    
    Double-Check,保证线程安全,实例化的代码也只会执行一次。线程安全,Lazy Loading,效率高。

    7.似饿汉模式(静态内部类)【推荐用】

    public class Singeton{
        private Singleton(){}
    
        private static class SingletonInstance{
          private static final Singleton INSTANCE = new Singleton();
        }
        public static  Singleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }
    

    和懒汉模式类似,但有所区别,两者都采用了类装载的机制,保证初始化实例的只有一个线程,不同的地方在于,饿汉模式只要Singleton类被装载就会实例化,没有Lazy Loading,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化的地方,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以JVM保证了线程的安全

    优点:线程安全,Lazy-Loading,效率高

    7.枚举【推荐用】

    public enum Singeton{
        INSTANCE;
        public void anyMethod() {
        }
    }
    

    借助JDK1.5添加的枚举类实现单例模式。不仅避免了多线程同步问题,还能反正反序列化重新创建新的对象。卵秀飞

    优点:真的秀

    单例模式的优点

    系统内存改类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统的性能。

    单例模式的缺点

    当想实例化一个单例类的时候,必须记住使用相应的获取对象的方法,而不是直接new,可能给其它开发人员造成困扰

    单例模式适用场景

    • 需要频繁创建销毁的对象
    • 创建很耗资源,当使用频繁
    • 工具类对象
    • 频繁访问数据库或文件的对象
    为什么实例必须为静态,获取实例的方法也必须为静态?

    你只要弄明白单例模式是如何实现的,就能从本质上理解这个问题;
    单例模式实现过程如下:
    首先,将该类的构造函数私有化(目的是禁止其他程序创建该类的对象);
    其次,在本类中自定义一个对象(既然禁止其他程序创建该类的对象,就要自己创建一个供程序使用,否则类就没法用,更不是单例);
    最后,提供一个可访问类自定义对象的类成员方法(对外提供该对象的访问方式)。

    直白的讲就是,你不能用该类在其他地方创建对象,而是通过该类自身提供的方法访问类中的那个自定义对象。

    那么问题的关键来了,程序调用类中方法只有两种方式,①创建类的一个对象,用该对象去调用类中方法;②使用类名直接调用类中方法,格式“类名.方法名()”;
    上面说了,构造函数私有化后第一种情况就不能用,只能使用第二种方法。
    而使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此类自定义的实例变量也必须是静态的。
    这就是单例模式唯一实例必须设置为静态的原因。

    参考地址:

    +https://www.cnblogs.com/zhaoyan001/p/6365064.html

    相关文章

      网友评论

        本文标题:单例模式的实现

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