• Java设计模式-单例模式


    单例模式(singleton)有一下三个要点

    1. 某个类只能有一个实例
    2. 该类自行负责该实例的创建
    3. 该类负责向整个系统提供该实例

    为了保证单例类的实例只能由单例类自己创建,需要将单例类的构造方法设置成private私有,防止构造方法被其他类调用

    单例模式有饿汉式、懒汉式等实现方式,下面一一做介绍

    饿汉式

    饿汉式在类加载的时候就会创建static的实例

    1. public class Eager {
    2. private static final Eager instance = new Eager();
    3. private Eager() {
    4. }
    5. public static Eager getInstance() {
    6. return instance;
    7. }
    8. }

    懒汉式

    懒汉式将实例的创建延后到第一次使用的时候,这时候instance是空的,就创建出来,后面使用就不用再创建

    这里需要注意的是getInstance()方法中使用了synchronized关键字,保证该方法只能同步调用,防止在多线程环境下可能导致的创建出多个实例的情况

    1. public class Lazy {
    2. private static Lazy instance;
    3. private Lazy() {
    4. }
    5. public synchronized static Lazy getInstance() {
    6. if (instance == null) {
    7. instance = new Lazy();
    8. }
    9. return instance;
    10. }
    11. }

    懒汉式还有如下双重校验锁的写法

    至于为什么要用volatile关键字,说来话长,以后单独开一篇聊

    1. public class Lazy2 {
    2. private volatile static Lazy2 instance;
    3. private Lazy2 (){}
    4. public static Lazy2 getInstance() {
    5. if (instance == null) {
    6. synchronized (Lazy2.class) {
    7. if (instance == null) {
    8. instance = new Lazy2();
    9. }
    10. }
    11. }
    12. return instance;
    13. }
    14. }

    使用内部类

    这种方式其实也可以归为懒汉式,因为是等到第一次使用的时候才创建对象

    1. public class Lazy3 {
    2. private static class InstanceHolder {
    3. private static Lazy3 instance = new Lazy3();
    4. }
    5. private Lazy3() {
    6. }
    7. public static Lazy3 getInstance() {
    8. return InstanceHolder.instance;
    9. }
    10. }

    由于Lazy3是InstanceHolder的类成员变量,因此在JVM调用InstanceHolder类的类构造器对其进行初始化时,虚拟机会保证一个类的类构造器在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器,其他线程都需要阻塞等待,直到活动线程执行方法完毕。

    JVM会保障多线程情况下类的正确初始化,所以借助这一点,我们创建一个内部类,然后让内部类初始化的时候创建唯一个实例作为成员变量

    登记式单例

    直接上代码

    父类的代码

    1. package com.himmy.gof.singleton;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. public class RegSingleton {
    5. private static Map mRegister = new HashMap<>();
    6. static {
    7. RegSingleton reg = new RegSingleton();
    8. mRegister.put(reg.getClass().getName(), reg);
    9. }
    10. protected RegSingleton() {
    11. }
    12. public static RegSingleton getInstance(String name) {
    13. if (name == null) {
    14. name = "com.himmy.gof.singleton.RegSingleton";
    15. }
    16. if (mRegister.get(name) == null) {
    17. try {
    18. mRegister.put(name, (RegSingleton) Class.forName(name).newInstance());
    19. } catch (Exception e) {
    20. e.printStackTrace();
    21. }
    22. }
    23. return mRegister.get(name);
    24. }
    25. public String about() {
    26. return "我是你爸爸";
    27. }
    28. }

    子类的代码

    1. package com.himmy.gof.singleton;
    2. public class RegSingletonChild extends RegSingleton {
    3. public RegSingletonChild() {
    4. }
    5. public static RegSingletonChild getInstance() {
    6. return (RegSingletonChild) RegSingleton.getInstance("com.himmy.gof.singleton.RegSingletonChild");
    7. }
    8. @Override
    9. public String about() {
    10. return "我是我爸爸的儿子";
    11. }
    12. }

    登记式单例比较少见,在GoF的《设计模式》一种中有提到该实现方式

    但是我个人不是很理解这种实现方式,不管是父类RegSingleton还是子类RegSingletonChild其构造方法都不是private,所以都不能保证在整个系统中只有一个唯一的实例,而这也就违反了单例模式的第一条要求“某个类只能有一个实例”

    有深入了解的朋友可以在评论里讨论下

    参考文章

    java多线程(三):多线程单例模式,双重检查,volatile关键字

  • 相关阅读:
    Excel如何设置密码保护【图文详情】
    政企组织为什么更需要私有化的IM即时通讯平台?
    打印日志遇到的问题,logback与zookeeper冲突
    SQL 游标
    《C++ Primer Plus》第九章:内存模型和名称空间(2)
    云原生之k8s】k8s 亲和、反亲和、污点、容忍
    ZYNQ7020--AMP下裸机程序开发 <2>
    Echarts 柱状图的详细配置过程
    5-4计算一串字符的空格数字字符其他
    线上服务器老是卡,该如何优化?
  • 原文地址:https://blog.csdn.net/mqdxiaoxiao/article/details/126526258