设计模式之单例模式

简介

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

七种实现方式

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

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

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

    }  
}

推荐使用

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu3 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile3 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白803 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙3 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发3 天前
软件工程SOLID 五大设计原则
后端·软件工程