设计模式之单例模式

简介

所谓的单例设计模式,就是采取一定的办法保证整个系统中,对某个类只存在一个对象实例,并且该类只提供一个获取该对象实例的方法,同时这个方法是静态的。 使用场景: 需要频繁的进行创建和销毁的对象、创建对象耗时很多,但是经常使用的对象、频繁访问数据库或文件的对象。

七种实现方式

第一种---饿汉式(静态常量)现方式

所谓饿汉式,通俗来讲就是我饿了,你得给我准备好,我想吃了就直接拿着吃了,别现做~ 过程如下

  • 1、构造器私有化(防止new创建)
  • 2、在类的内部创建对象
  • 3、向外暴露一个静态的公共方法 下面是代码实现
java 复制代码
public class StaticConstant {  
  
    /**  
    * 构造方法私有化  
    */  
    private StaticConstant(){}  

    /**  
    * 静态常量实例对象  
    */  
    private static final StaticConstant staticConstant = new StaticConstant();  

    /**  
    * 对外暴露的静态方法  
    * @return  
    */  
    public static StaticConstant getInstance(){  
        return staticConstant;  
    }  
}

优点

写法简单,类装载的时候就完成了实例化,没有线程安全问题。

缺点

在类装载的时候就完成了实例化,没有达到懒加载的效果,如果没有使用过这个实例,其实就是一种内存浪费。

第二种---饿汉式(静态代码块)实现方式

  • 1、构造器私有化(防止new创建)
  • 2、在类的静态代码块中创建实例对象
  • 3、向外暴露一个静态的公共方法
java 复制代码
public class StaticCodeBlock {  
  
    /**  
    * 构造方法私有化  
    */  
    private StaticCodeBlock(){}  

    /**  
    * 静态常量实例对象  
    */  
    private static StaticCodeBlock staticCodeBlock;  

    /**  
    * 静态代码块  
    */  
    static {  
    staticCodeBlock = new StaticCodeBlock();  
    }  

    /**  
    * 对外暴露的静态方法  
    * @return  
    */  
    public static StaticCodeBlock getInstance(){  
        return staticCodeBlock;  
        }  
    }

优点和缺点和第一种方式一样!!

第三种---线程不安全的懒汉式实现方式

  • 1、构造器私有化(防止new创建)
  • 2、向外暴露一个静态的公共方法
  • 3、在公共方法是完成实例对象的创建,但是需要判断是否已经创建过 代码如下
java 复制代码
public class ThreadNoSafe {  
  
    /**  
    * 构造方法私有化  
    */  
    private ThreadNoSafe(){}  

    /**  
    * 静态常量实例对象  
    */  
    private static ThreadNoSafe threadNoSafe;  

    /**  
    * 对外暴露的静态方法  
    * @return  
    */  
    public static ThreadNoSafe getInstance(){  
        if(threadNoSafe == null){  
            threadNoSafe = new ThreadNoSafe(); 
        }  
        return threadNoSafe;  
    }  
}

优点

起到了懒加载的效果

缺点

只能在单线程的情况下使用,我们可以看到在if(threadNoSafe == null)这行语句中,多线程的环境下,一个线程完成了这个判断,但是还没有执行向下的代码,另一个线程也进来了,也执行判断,判断的结果则也是null,所以就会存在多个线程同时执行创建的情况,那么就会产生多个实例对象,所以实际开发中不要使用这种方式。

第四种---线程安全的懒汉式实现方式

步骤同第三种,但是在getInstance方法上加上sychronized 代码如下

java 复制代码
public class ThreadSafe {  
  
    /**  
    * 构造方法私有化  
    */  
    private ThreadSafe(){}  

    /**  
    * 静态常量实例对象  
    */  
    private static ThreadSafe threadSafe;  

    /**  
    * 对外暴露的静态方法  
    * @return  
    */  
    public static synchronized ThreadSafe getInstance(){  
        if(threadSafe == null){  
            threadSafe = new ThreadSafe();  
        }  
        return threadSafe;  
    }  
}

优点

解决了线程不安全的问题。

缺点

效率太低了,每个线程想要获取类的实例的时候都需要同步。

第五种---线程安全的懒汉式(双重检查)实现方式

过程同四类似,但是sychronized换了位置,代码如下

java 复制代码
public class ThreadSafeDoubleCheck {  
  
/**  
* 构造方法私有化  
*/  
private ThreadSafeDoubleCheck(){}  
  
    /**  
    * 静态常量实例对象  
    */  
    private static ThreadSafeDoubleCheck threadSafeDoubleCheck;  

    /**  
    * 对外暴露的静态方法  
    * @return  
    */  
    public static synchronized ThreadSafeDoubleCheck getInstance(){  
        if(threadSafeDoubleCheck == null){  
            synchronized (ThreadSafeDoubleCheck.class) {  
                if(threadSafeDoubleCheck == null) {  
                    threadSafeDoubleCheck = new ThreadSafeDoubleCheck();  
                }  
            }  
        }  
        return threadSafeDoubleCheck;  
    }  
}

这个是推荐使用的一种方式,我们可以看到这是一个双重检查,尽管第一次判断有多个线程进入,那么进入第二个判断的也只有一个线程,这个线程创建完之后,其他线程进入判断的时候,实例对象就不是空的了,所以是双重检查的单例对象的创建方式是线程安全的,也是推荐使用的。

第六种---静态内部类的实现方式(线程安全)

在类加载时候内部类是不会加载的,而是当调用到内部类的时候才会去加载,同时类加载的时候是线程安全的

  • 1、构造器私有化(防止new创建)
  • 2、在类的内部编写一个内部类,内部类中创建一个当前类的常量对象。
  • 3、向外暴露一个静态的公共方法,返回内部类中的常量对象实例。 代码如下
java 复制代码
public class InnerClass {  
  
    /**  
    * 构造方法私有化  
    */  
    private InnerClass(){}  

    /**  
    * 内部类  
    */  
    private static class SingletonClass{  
        private static final InnerClass instance = new InnerClass();  
    }  

    /***  
    * 对外暴露的static方法  
    * @return  
    */  
    public static InnerClass getInstance(){  
        return SingletonClass.instance;  
    }  
}

JVM帮我们保证了线程安全,同时利用静态内部类的特点实现了延迟加载,推荐使用。

第七种--- 枚举方式实现(线程安全)

借助枚举实现单例,不仅能避免多线程的问题,还能方式反序列化创建新的对象。代码如下

java 复制代码
public enum EnumSingleton {  
  
INSTANCE;  
  
    public void test(){  

    }  
}

推荐使用

相关推荐
qq_2975746716 分钟前
【实战】POI 实现 Excel 多级表头导出(含合并单元格完整方案)
java·spring boot·后端·excel
郝学胜-神的一滴28 分钟前
超越Spring的Summer(一): PackageScanner 类实现原理详解
java·服务器·开发语言·后端·spring·软件构建
Tony Bai31 分钟前
“Go 2,请不要发生!”:如果 Go 变成了“缝合怪”,你还会爱它吗?
开发语言·后端·golang
Victor3561 小时前
Hibernate(91)如何在数据库回归测试中使用Hibernate?
后端
Victor3561 小时前
MongoDB(1)什么是MongoDB?
后端
Victor3567 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor3567 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端
灰子学技术9 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
Gogo81610 小时前
BigInt 与 Number 的爱恨情仇,为何大佬都劝你“能用 Number 就别用 BigInt”?
后端
fuquxiaoguang10 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析