设计模式之单例模式

简介

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

七种实现方式

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

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

  • 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(){  

    }  
}

推荐使用

相关推荐
星释1 小时前
Rust 练习册 :Leap与日期计算
开发语言·后端·rust
xiaodaidai丶4 小时前
设计模式之策略模式
设计模式·策略模式
_院长大人_5 小时前
设计模式-工厂模式
java·开发语言·设计模式
码事漫谈5 小时前
C++死锁深度解析:从成因到预防与避免
后端
码事漫谈5 小时前
智能体颠覆教育行业:现状、应用与未来展望调研报告
后端
蓝-萧5 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
priority_key5 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
韩立学长5 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
汤姆yu7 小时前
基于SpringBoot的动漫周边商场系统的设计与开发
java·spring boot·后端
灰小猿7 小时前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud