设计模式-单例模式(一)-创新互联

单例模式
  • 一 官方定义
  • 二 单例模式八种方式
    • 2.1 饿汉式(静态常量)
      • 代码案例
      • 案例分析
    • 2.2 饿汉式(静态代码块)
      • 代码案例
      • 案例分析
    • 2.3 懒汉式(线程不安全)
      • 代码案例
      • 案例分析
    • 2.4 懒汉式(线程安全,同步方法)
      • 代码案例
      • 案例分析
    • 2.5 懒汉式(线程不安全,同步代码块)
      • 代码案例
      • 案例分析
    • 2.6 双重检查 (推荐使用)
      • 代码案例
      • 案例分析
        • 优点
        • 可能出现的问题
        • 扩展 - Volatile
    • 2.7 静态内部类 (推荐使用)
      • 代码案例
      • 案例分析
    • 2.8 枚举方式
      • 代码案例
      • 案例分析
    • 三 注意事项
    • 四 单例模式的使用场景

创新互联建站专业网站制作、网站设计,集网站策划、网站设计、网站制作于一体,网站seo、网站优化、网站营销、软文推广等专业人才根据搜索规律编程设计,让网站在运行后,在搜索中有好的表现,专业设计制作为您带来效益的网站!让网站建设为您创造效益。一 官方定义

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)

Spring 中的 bean 默认都是单例模式,每个bean定义只生成一个对象实例,每次 getBean请求获得的都是此实例

二 单例模式八种方式

单例模式的八种实现方式,如下所示

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举方式
2.1 饿汉式(静态常量) 代码案例
class Singleton {//一:构造器的私有化    防止外部用构造器...
    private Singleton() {}

    //二:类的内部创建对象    final static
    private static final Singleton singleton = new Singleton();

    //三:对外提供公共的静态方法    返回该类唯一的对象实例
    public static Singleton getInstance() {return singleton;
    }
}
案例分析
//案例演示 - 饿汉式
public class SingletonDemo {public static void main(String[] args) {//      方式一:静态常量
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1); //true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
}

在这里插入图片描述
通过结果可以发现返回的是同一个对象所以单例模式是实现的。

写法分析
优势: 简单 避免多线程的同步问题
劣势: 没有达到懒加载的效果 内存的浪费

2.2 饿汉式(静态代码块) 代码案例
//方式二:静态代码块的方式
class Singleton {//构造器私有化
    private Singleton() {}

    //类的内部创建对象
    private static final Singleton singleton;

    static {singleton = new Singleton();
    }

    //对外提供公共的静态的方法
    public static Singleton getInstance() {return singleton;
    }
}
案例分析

优势: 简单 避免多线程的同步问题
劣势: 没有达到懒加载的效果 内存的浪费

2.3 懒汉式(线程不安全) 代码案例
class Singleton{//构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static Singleton getInstance(){if (singleton == null){singleton = new Singleton();
        }

        return singleton;
    }

}
案例分析

优势:起到了懒加载的效果 不会造成内存浪费

在使用到的时候才会创建对象。判断有无对象,有则返回,无创建后再返回

劣势:只能在单线程下使用,多线程情况下线程不安全 不推荐这种方式的
在这里插入图片描述

① 在多线程的情况下,有一个对象进入if判断通过,还没执行到创建对象这一步骤时
② 有另外一个对象也进入了if判断,也通过了。
此时就会出现多个实例,造成线程不安全。

2.4 懒汉式(线程安全,同步方法)

在获取对象的静态方法上添加 synchronized 关键字,实现同步方法。解决线程不安全问题。

代码案例
//加入同步处理  同步方法
class Singleton{//构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static synchronized Singleton getInstance(){if (singleton == null){singleton = new Singleton();
        }

        return singleton;
    }

}
案例分析

解决了线程安全问题,但是效率太低

每一个线程需求拿实例的时候都要在外面等候另一线程处理完

2.5 懒汉式(线程不安全,同步代码块)

将实例化对象过程放入同步代码块中

代码案例
//加入同步处理 - 同步代码块的方式  不推荐的
class Singleton{//构造器私有化
    private Singleton(){}

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共的静态方法的时候,来判断
    public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){singleton = new Singleton();
            }

        }

        return singleton;
    }

}
案例分析

不推荐的,解决不了线程的安全问题

这种方式本意是想解决同步方法的问题。但是我们分析一下。在多线程情况下还是有可能创建多个实例的问题。

2.6 双重检查 (推荐使用) 代码案例
class Singleton{private Singleton(){}
    //禁止指令重排
    private static volatile Singleton singleton;
    //加入双重检查机制
    public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){if (singleton == null){singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}
案例分析 优点

线程安全

解决了线程安全问题
① 多线程时,若两个线程同时满足第一层非空判断,等待调用同步代码块。
② 由于添加了synchronized ,当第一个线程进来时,发现Singleton对象为空,进行创建对象并返回。
③ 当另外一个线程调用同步代码块时,进行再次对象非空判断,发现对象已经创建成功。不在执行创建对象流程,返回已有对象

懒加载

使用到对象时才创建,不会造成内存的浪费

效率很高

先进性了singleton 是否为空的判断,singleton 如果不为空直接返回结果。倘若无第一层判空,多线程时每次都要进行synchronized 等待其他线程处理结束,才能进入内部非空判断,效率相对低。

可能出现的问题

我们认为的 new Singleton() 操作

1)分配内存地址 M
2)在内存 M 上初始化Singleton 对象
3)将M的地址赋值给 instance 对象

JVM编译优化后(指令重排)可能的 new Singleton() 操作

1)分配内存地址 M
2)将M的地址赋值给instance变量
3)在内存M上初始化 Singleton 对象

这就有可能出现空指针异常
异常发生过程
在这里插入图片描述
解决方式:关键字 Volatile 来禁止指令重排

扩展 - Volatile

轻量级的同步机制 (低配版) 没有保证原子性
三大特性
保证可见性
其中一个线程修改了主内存共享变量的值,要写回主内存,并要及时通知其他线程可见
没有保证原子性
没法(不能保证)不可分割,完整,要么同时成功,要么同时失败
禁止指令重排
和底层内存屏障相关 避免多线程下出现指令乱序的情况

扩展-线程切换

Java的一条语句对应的cpu指令可能是多条,其中任意一条cpu指令在执行完都可能发生线程切换
count += 1,对应cpu 指令如下:

1)将变量count从内存加载到cpu寄存器
2)寄存器中 +1
3)将结果写入内存(缓存机制写入的可能是cpu而不是内存)
在这里插入图片描述

2.7 静态内部类 (推荐使用) 代码案例
class Singleton{private Singleton(){}

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

    public static Singleton getInstance(){return SingletonInstance.INSTANCE;
    }
}
案例分析

①当Singleton类加载时,静态内部类是不会加载的。
②只有调用getInstance方法用到了SingletonInstance.INSTANCE静态变量时导致SingletonInstance静态内部类进行加载。

不会出现线程安全问题
JVM来帮我们保证了线程的安全性
利用静态内部类的特点,效率也很高,实际开发中推荐使用的

2.8 枚举方式 代码案例
public class EnumDemo {public static void main(String[] args) {//验证其正确性
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;
        System.out.println(instance == instance1); //true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());

    }
}

enum Singleton{INSTANCE; //属性
}
案例分析

不仅可以避免线程安全问题 还可以防止反序列化重新创建对象。推荐使用

三 注意事项
  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
四 单例模式的使用场景
  • 对于一些需要频繁创建销毁的对象
  • 重量级的对象(创建对象时耗时过多,或者耗费资源过多)
  • 经常使用到的对象
  • 工具类对象
  • 数据源,session。。。。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前名称:设计模式-单例模式(一)-创新互联
网站路径:http://pwwzsj.com/article/djdeec.html