设计模式之单例模式

简介

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

七种实现方式

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

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

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

    }  
}

推荐使用

相关推荐
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
WaaTong1 小时前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠3 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
WaaTong3 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
AskHarries3 小时前
Java字节码增强库ByteBuddy
java·后端
霁月风3 小时前
设计模式——观察者模式
c++·观察者模式·设计模式
佳佳_3 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端