懒汉式和饿汉式单例模式以及解决单例的线程不安全

时间:2024-03-15 14:12:06

懒汉式和饿汉式是两种常见的单例模式实现方式,它们分别在不同情况下进行单例对象的初始化。以下是关于懒汉式和饿汉式单例模式的简要解释:

1. 懒汉式单例模式

  • 概念:懒汉式单例模式是指在首次使用时才会创建单例对象。

  • 特点

    • 在多线程环境下可能存在线程安全问题,需要通过加锁或双重检查锁机制来保证线程安全。
    • 第一次获取实例时会稍微耗费一些时间,因为需要在运行时创建对象。
  • 示例代码

    public class Singleton {
        private static Singleton instance;
    
        private Singleton() {}
    
        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

2. 饿汉式单例模式

  • 概念:饿汉式单例模式是指在类加载时就创建并初始化单例对象。

  • 特点

    • 线程安全,因为实例在类加载时已经创建,不会存在多线程竞争创建实例的问题。
    • 应用启动时会立即创建实例,可能导致资源浪费。
  • 示例代码

    public class Singleton {
        private static Singleton instance = new Singleton();
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    

3. 懒汉式单例 + 双重检查锁

  • 特点

    • 在第一次获取实例时才会创建单例对象。
    • 通过双重检查来减少加锁的开销,提高性能。
    • 确保在多线程环境下安全地进行单例对象的初始化。
  • 示例代码

    public class Singleton {
        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;
        }
    }
    

2. 双重检查锁原理

  • 第一次检查:检查实例是否已经被创建,如果没有,则进入同步代码块。
  • 同步代码块:使用 synchronized 关键字对实例化代码块进行加锁,确保只有一个线程可以进入临界区。
  • 第二次检查:在同步代码块内再次检查实例是否已经被创建,防止多个线程同时通过第一次检查后,造成重复实例化的问题。

双重检查锁机制通过在必要时才加锁的方式,避免了每次获取实例都进行同步的开销,提高了性能。需要注意的是,在 Java 5 及以上版本中,要将 instance 声明为 volatile,以确保在多线程情况下 instance 的可见性,并避免指令重排序导致的问题。采用双重检查锁机制可以有效地保证单例对象在多线程环境下的安全性和性能。

选择懒汉式还是饿汉式单例模式取决于具体需求,如果希望延迟加载、避免资源浪费,并且可以处理好多线程并发安全问题,可以选择懒汉式;如果希望简单、线程安全,并且无需延迟加载,可以选择饿汉式。在实际应用中,根据具体场景和需求选用合适的单例模式实现方式。