java 项目中设计模式 之单例模式

本文是自己学习单例模式的记录,方便以后使用的时候查看,并不专业哈

一、单例模式

1、特点:

  • 单例模式是最简单的一种模式,具有单例模式只能有一个实例,并且必须为其他对象提供一个访问点。
  • 单例模式分为懒汉模式和饥饿模式

二、单例模式的创建方法

顾名思义,懒汉式单例模式会在使用时才生成实例,不会提前生成实例。

1、懒汉模式(线程不安全)

java 复制代码
public class Singleton {
    //在类内部声明一个类对象
       private static Singleton instance;


    private Singleton() {
           // 私有构造方法
       }

    /** 
     * 向外部提供一个获取对象的方法,如果该类对象不存在,创建该对象,
     * 如果该类对象已经存在,就直接返回该对象。保证该类对象只有一个,存在后就不再创建
     * 方法上使用synchronized锁,为了保证如果多线程调用,会同步执行,防止对象多次创建 
     */
       public static Singleton getInstance() {
           if (instance == null) {
               instance = new Singleton();
           }
           return instance;
       }
   }

注: 该方式线程不安全,如果有多线程同时调用,会创建多个实例

2、懒汉模式(线程安全)

java 复制代码
public class Singleton2 {
    //在类内部声明一个类对象
       private static Singleton2 instance;


    private Singleton2() {
           // 私有构造方法
       }

    /** 
     * 向外部提供一个获取对象的方法,如果该类对象不存在,创建该对象,
     * 如果该类对象已经存在,就直接返回该对象。保证该类对象只有一个,存在后就不再创建
     * 方法上使用synchronized锁,为了保证如果多线程调用,会同步执行,防止对象多次创建 
     */
       public static synchronized  Singleton2 getInstance() {
           if (instance == null) {
               instance = new Singleton2();
           }
           return instance;
       }
   }

注: synchronized 是为了保证多线程时可调用,但是也会影响性能

3、饥饿模式(静态变量)

顾名思义,饿汉式单例模式会在类加载时就会生成实例,且保证实例只有一个。

java 复制代码
public class Singleton3 {
    //使用static声明变量的时候就new了实例,保证实例在类加载时就创建
    private static final Singleton3 instance = new Singleton3();
    
    private Singleton3() {
        // 私有构造方法
    }

    //提供一个供外部对象获取实例的入口
    public static Singleton3 getInstance() {
        return instance;
    }
   }

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading 懒加载的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费

4、饥饿模式(静态代码块)

java 复制代码
public class Singleton4 {

    //构造器私有化,外部不能new
    private Singleton4() {
    }

    //声明对象
    private static Singleton4 instance;
    //在静态代码块中创建对象实例
    static {
        instance = new Singleton4();
    }

    //提供一个公有的静态方法,返回对象实例
    public static Singleton4 getInstance() {
        return instance;
    }
}

5、懒汉模式(线程安全同步代码块)

java 复制代码
public class Singleton5 {

    //懒汉式(线程安全, 同步代码块)
    private static Singleton5 instance;
    //构造方法
    private Singleton5() {
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance 即懒汉式
    public static Singleton5 getInstance() {
        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = new Singleton4();
            }
        }
        return instance;
    }

}

这种方式,是对懒汉模式的优化,因为前面同步方法效率太低,改为同步产生实例化的的代码块

但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (instance == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例

6、懒汉式(双重检查)

java 复制代码
//双重检查
public class Singleton6 {
    private Singleton6() {}
    private static volatile Singleton6 instance;
    public static Singleton6 getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if (instance == null){
            synchronized(Singleton6.class){
                //抢到锁之后再次判断是否为空
                if (instance == null){
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

注:volatile关键字的作用:

  1. 线程的可见性,当一个线程修改了共享变量时,另一个线程可以立刻读到该变量修改后的值
  2. 顺序一致性,禁止指令重排序。
  • Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (instance == null)检查,这样就可以保证线程安全了
  • 添加 volatile 关键字之后可以很好解决双重检查锁模式带来空指针异常的问题,能够保证在多线程的情况下线程安全也不会有性能问题
  • 线程安全;延迟加载;效率较高。在实际开发中,推荐使用这种单例设计模式

7、懒汉式(静态内部类)

java 复制代码
//静态内部类
public class Singleton7 {
    private Singleton7() {}
    //提供一个静态的公有方法,当使用到该方法时,才去创建instance 即懒汉式
    private static class SingletonInstance {
        private static final Singleton7 INSTANCE = new Singleton7();
    }
    public static Singleton7 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
  • 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
  • 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
  • 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  • 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高

8、饿汉式(枚举)

java 复制代码
public enum Singleton8 {
    // 枚举实例在类加载时创建(饿汉式)
    INSTANCE;

    // 添加业务方法
    public void doSomething() {
        System.out.println("Singleton is working!");
    }
}


//使用示例
public class Main {
    public static void main(String[] args) {
        // 调用单例方法
        Singleton8.INSTANCE.doSomething();

        // 验证单例性
        Singleton s1 = Singleton8.INSTANCE;
        Singleton s2 = Singleton8.INSTANCE;
        System.out.println(s1 ==s2); // 输出 true
    }
}

INSTANCE 的含义

名称约定:INSTANCE 是一个标识符(通常用全大写),表示这是单例模式的唯一实例。你可以自定义名称(例如 SINGLETON、INST),但 INSTANCE 是常见写法。

核心原理

特性 说明
类加载时初始化 枚举实例 INSTANCE 在类加载阶段被初始化,符合饿汉式的特性。
天然单例性 枚举类型规定每个枚举常量都是唯一的,且 JVM 保证全局只有一个实例。
防反射攻击 反射调用 Constructor.newInstance() 会抛出异常(枚举无法通过反射实例化)。
防序列化破坏 枚举的序列化机制由 Enum.valueOf() 保证,直接返回已有实例,而非新建。
相关推荐
源码code18 分钟前
基于微信小程序的停车场管理系统的设计与实现
java·python·spring·tomcat
心灵宝贝28 分钟前
Linux中jdk-8u291-linux-x64 中jdk工具包
java·linux·开发语言
小钊(求职中)1 小时前
最新Git入门到精通完整教程
java·git·后端·spring·面试
三天不学习1 小时前
23种设计模式之单例模式(Singleton Pattern)【设计模式】
java·单例模式·设计模式·c#
大彬聊编程1 小时前
京东一面:为什么 IDEA 建议去掉 StringBuilder,而要使用 “+” 拼接字符串?
java
java技术小馆1 小时前
IDEA 接入 Deepseek
java·ide·intellij-idea·ai编程
花花进修1 小时前
Java设计模式——建造者模式
设计模式·建造者模式
WangYaolove13141 小时前
java2025热点面试题之springmvc
java·springmvc
程序视点1 小时前
JVM虚拟机监控及性能调优实战
java·jvm·后端
橘猫云计算机设计2 小时前
基于微信小程序的疫情互助平台(源码+lw+部署文档+讲解),源码可白嫖!
java·大数据·开发语言·spring boot·微信小程序·小程序·汽车