单例模式详解

文章目录

一、概述

1.单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要一个对象被共享且全局唯一的情况下非常有用,比如配置对象、日志对象、数据库连接对象等。

2.单例模式的特点

唯一性:保证一个类只有一个实例。

全局访问点:提供一个全局访问点来获取该实例。

3.单例模式的实现方法

单例模式有多种实现方式,常见的有饿汉式、懒汉式、双重校验锁、静态内部类和枚举等。以下是这些方法的详细实现及其优缺点。

二、单例模式的实现

1. 饿汉式

饿汉式单例模式在类加载时就初始化实例,因此线程安全。

java 复制代码
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
        // 私有构造函数,防止实例化
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

优点:

  • 实现简单。
  • 线程安全。

缺点:

  • 类加载时就实例化,可能造成资源浪费。

2. 懒汉式

懒汉式单例模式在第一次使用时才初始化实例,不是线程安全的,需要加锁。

java 复制代码
public class Singleton {
    private static Singleton instance;
    
    private Singleton() {
        // 私有构造函数,防止实例化
    }
    
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点:

  • 延迟加载,节省资源。

缺点:

  • 需要加锁,性能较差。
  • 每次获取实例时都需要同步,开销较大。

3. 双重校验锁

双重校验锁在懒汉式的基础上进行了优化,减少了不必要的同步开销。

java 复制代码
public class Singleton {
    // 使用 volatile 关键字确保 instance 的可见性和有序性
    private static volatile Singleton instance;

    private Singleton() {
        // 私有构造函数,防止实例化
    }

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查(无锁)
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查(有锁)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:

  • 延迟加载,节省资源。
  • 线程安全,性能较高。

缺点:

  • 代码较复杂。
  • 需要使用 volatile 关键字,确保可见性和有序性。

解释步骤:
1、为什么双重🔐

第一次检查(无锁):

if (instance == null):第一次检查是否已经有实例。如果有,则直接返回实例,避免进入同步块,提高性能。

同步块:synchronized (Singleton.class):如果第一次检查发现实例为null,进入同步块,确保只有一个线程能够进入创建实例的代码。

第二次检查(有锁):

if (instance == null):再次检查实例是否为null。这是因为在同步块外的第一次检查和进入同步块之间,可能有另一个线程已经创建了实例。
2、为什么需要 volatile

使用 volatile 关键字确保 instance 的可见性和有序性。它防止了指令重排序可能导致的问题。在没有 volatile 的情况下,可能会发生如下情况:

分配内存。

初始化对象。

设置 instance 指向分配的内存。

指令重排序可能会使步骤2和步骤3的顺序颠倒,使得一个线程看到一个未完全初始化的对象。因此,volatile 确保了步骤2和步骤3的顺序不会被颠倒。

4. 静态内部类

利用静态内部类的特性实现单例模式,线程安全且延迟加载。

java 复制代码
public class Singleton {
    private Singleton() {
        // 私有构造函数,防止实例化
    }
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:

  • 延迟加载,节省资源。
  • 线程安全,利用类加载机制保证初始化时只有一个线程。

缺点:

  • 代码较简单,易读性好。

5. 枚举

利用枚举实现单例模式,天生线程安全,防止反序列化创建新实例。

java 复制代码
public enum Singleton {
    INSTANCE;
    
    public void someMethod() {
        // 单例方法
    }
}

// 测试类
	public static void main(String[] args) {
		// 创建枚举对象
		SingletonEnum instance1 = SingletonEnum.INSTANCE;
		SingletonEnum instance2 = SingletonEnum.INSTANCE;
		
		System.out.println( instance1== instance2);
		instance1.run();
		//System.out.println(SingletonEnum.INSTANCE);		
	}

优点:

  • 实现简单。
  • 天生线程安全。
  • 防止反序列化创建新实例。

缺点:

  • 不能延迟加载(可以通过懒初始化方法来实现)。

三、总结

单例模式通过确保一个类只有一个实例,提供了一种全局访问点来访问该实例。根据不同的需求和场景,可以选择不同的实现方式。

饿汉式和枚举方式实现简单,但不能延迟加载;懒汉式和双重校验锁方式可以延迟加载,但需要考虑线程安全问题;静态内部类方式兼具延迟加载和线程安全,是一种推荐的实现方式。

单例模式的特点:

1.构造方法私有化(即构造方法被private修饰)

2.该单例对象必须由单例类自行创建

3.内部提供一个公共静态的方法给外界进行访问(方法被public static 修饰)

相关推荐
雷渊1 分钟前
spring-IoC容器启动流程源码分析
java·后端·面试
用户3315489111076 分钟前
一招搞定Java线程池炸弹,系统吞吐量暴增10倍!
java·后端
卡戎-caryon10 分钟前
【Linux网络与网络编程】03.UDP Socket编程
linux·服务器·网络·笔记·单例模式·udp·网络通信
努力的搬砖人.11 分钟前
maven如何使用
java·后端·面试·maven
风象南14 分钟前
SpringBoot中6种跨域请求解决方案
java·spring boot·后端
vivo互联网技术16 分钟前
活动中台系统慢 SQL 治理实践
java·数据库·后端
是小李呀~27 分钟前
【工作梳理】怎么把f12里面的东西导入到postman
java
攀小黑27 分钟前
Java 多线程加锁 synchronized 关键字 字符串当做key
java·开发语言
huang_xiaoen29 分钟前
java设计模式之桥接模式(重生之我在地府当孟婆)
设计模式·桥接模式
余华余华40 分钟前
2024年蓝桥杯Java B组省赛真题超详解析-分布式队列
java·职场和发展·蓝桥杯