重学 Java 设计模式:实战单例模式

简介: 重学 Java 设计模式:实战单例模式

重学 Java 设计模式:实战单例模式

Java 设计模式中的单例模式旨在确保某个类在整个项目中只有一个实例,并且提供一个全局访问点,方便我们在其他类中调用。

本文将通过实战的方式,介绍七种常见的单例模式,并详细阐述其特点、优点和适用场景。最后我们将介绍 Effective Java 作者推荐的枚举单例模式,并介绍其优点和实现方式。

饿汉式单例模式

饿汉式单例模式的实现方式非常简单,即在类加载时就创建好对象实例,这样可以确保在多线程下也能够保持实例唯一性。以下是其实现代码:

public class SingletonOne {
    // 在类加载时就创建好对象实例
    private static final SingletonOne INSTANCE = new SingletonOne();
    // 构造方法私有化,防止其他类创建实例
    private SingletonOne() {}
    // 提供全局访问点,返回唯一实例
    public static SingletonOne getInstance() {
        return INSTANCE;
    }
}

饿汉式单例模式的优点是实现简单、线程安全,不存在多线程下的单例对象创建问题,适用于单线程或者初始化时间比较短的情况。但是缺点也很明显,就是会在类加载时进行实例化。如果这个单例比较复杂、需要一定的耗时才能完成初始化,那么程序启动的时间将会很长。

懒汉式单例模式

懒汉式单例模式是在调用获取实例方法时才进行对象的实例化,它实现了懒加载的效果,节省了程序启动时间。但是,在多线程下会出现单例对象创建的问题,需要加上同步锁来保证线程安全。以下是懒汉式单例模式的实现代码:

public class SingletonTwo {
    // 需要在调用获取实例方法时才创建实例
    private static SingletonTwo INSTANCE = null;
    // 构造方法私有化,防止其他类创建实例
    private SingletonTwo() {}
    // 提供全局访问点,返回唯一实例
    public static synchronized SingletonTwo getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SingletonTwo();
        }
        return INSTANCE;
    }
}

懒汉式单例模式的优点是实现了懒加载,节省了程序启动时间。缺点是需要加上同步锁来保证线程安全,每次获取实例时都需要进行同步,如果在高并发场景下,会降低系统性能。

双重检查锁单例模式

双重检查锁单例模式通过双重检查锁和 volatile 关键字来确保线程安全。在进行 instance 实例化时,通过两次判断加锁的方式来确保只有一个实例。以下是双重检查锁单例模式的实现代码:

public class SingletonThree {
   // 需要在调用获取实例方法时才创建实例
   private static volatile SingletonThree INSTANCE = null;
   // 构造方法私有化,防止其他类创建实例
   private SingletonThree() {}
   // 提供全局访问点,返回唯一实例
   public static SingletonThree getInstance() {
       if (INSTANCE == null) {
           synchronized (SingletonThree.class) {
               if (INSTANCE == null) {
                   INSTANCE = new SingletonThree();
               }
           }
       }
       return INSTANCE;
   }
}

双重检查锁单例模式的优点是通过双重检查锁和 volatile 关键字来确保线程安全,兼顾了懒加载和线程安全两个特点。但是由于需要进行双重检查锁,因此会降低系统性能,在单例比较简单的情况下,建议使用懒汉式单例模式。

静态内部类单例模式

静态内部类单例模式的实现方式结合了懒加载和线程安全的特点,在调用 getInstance 方法时才实例化对象,同时利用 JDK 特性保证了线程安全。以下是静态内部类单例模式的实现代码:

public class SingletonFour {
        // 静态内部类只会在第一次被使用的时候加载
        private static class SingletonHolder {
            private static final SingletonFour INSTANCE = new SingletonFour();
        }
        // 构造方法私有化,防止其他类创建实例
        private SingletonFour() {}
        // 提供全局访问点,返回唯一实例
        public static SingletonFour getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }

静态内部类单例模式的优点是实现了懒加载和线程安全,兼顾了多种情况下的需求。缺点是使用了静态内部类,增加了代码的复杂度。

枚举单例模式

Effective Java 作者推荐的枚举单例模式是实现单例模式的最佳方式。Java 枚举天生具有单例模式的特征,因此只需要将构造方法私有化,并将其设置为枚举类型即可实现单例模式。以下是枚举单例模式的实现代码:

public enum SingletonFive {
        INSTANCE;
        // 构造方法只会在枚举类型初始化时执行一次
        private SingletonFive() {}
        // 提供全局访问点,返回唯一实例
        public static SingletonFive getInstance() {
            return INSTANCE;
        }
    }

枚举单例模式的优点是枚举类天生单例模式,线程安全、防止反序列化重新创建新的对象,同时实现简单、代码量少。缺点是不支持懒加载和延迟加载,使用枚举类实现的单例模式不能被继承。

ThreadLocal 单例模式

ThreadLocal 单例模式维护了一个线程变量,在每个线程中都拥有一个单独的实例。通过 ThreadLocal 保证了线程安全,并且实现了懒加载的效果。以下是 ThreadLocal单例模式的实现代码:

public class SingletonSix {
        // 通过 ThreadLocal 维护了一个线程变量,并在需要时才实例化对象
        private static ThreadLocal<SingletonSix> THREAD_LOCAL = ThreadLocal.withInitial(SingletonSix::new);
        // 构造方法私有化,防止其他类创建实例
        private SingletonSix() {}
        // 提供全局访问点,返回唯一实例
        public static SingletonSix getInstance() {
            return THREAD_LOCAL.get();
        }
    }

ThreadLocal 单例模式的优点是实现了懒加载、线程安全,并且在多线程下也能运行良好。缺点是可能在某些情况下导致资源占用过多,在实际使用过程中需要加以注意。

容器单例模式

容器单例模式又叫注册式单例模式,将每个实例都注册到一个容器中,再需要使用时从容器中获取实例对象。使用容器单例模式可以动态地注册和获取实例对象,同时也可以保证实例的唯一性。以下是容器单例模式的实现代码:

public class SingletonContainer {
        // 使用 ConcurrentHashMap 保证线程安全
        private static Map<String, Object> singletonMap = new ConcurrentHashMap<>();
        private SingletonContainer() {}
        // 向容器中注册实例对象
        public static void registerInstance(String key, Object instance) {
            if (!singletonMap.containsKey(key)) {
                singletonMap.put(key, instance);
            }
        }
        // 从容器中获取实例对象
        public static Object getInstance(String key) {
            return singletonMap.get(key);
        }
    }

容器单例模式的优点是实现了动态注册和获取实例对象,使用时可以直接从容器中取值,可保证实例对象唯一性。缺点是需要使用 ConcurrentHashMap等线程安全的容器,同时容器单例模式的实例对象是存储在容器中的,需要耗费一定的内存空间。

综上所述,通过以上七种实战方式,我们详细介绍了 Java 单例模式的实现及其优缺点。在实际应用中,我们需要根据具体场景灵活地选择不同的单例模式,结合业务特点来设计实现。希望本文对您有所帮助,谢谢!

目录
相关文章
|
14小时前
|
设计模式 缓存 Java
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
11 4
|
15小时前
|
设计模式 Java 数据库
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
5 0
|
15小时前
|
设计模式 Java
Java设计模式:外观模式之优雅门面(九)
Java设计模式:外观模式之优雅门面(九)
4 0
|
15小时前
|
设计模式 安全 Java
Java设计模式:代理模式的静态和动态之分(八)
Java设计模式:代理模式的静态和动态之分(八)
7 0
|
22小时前
|
设计模式 Java 数据库连接
【Java设计模式 - 创建型模式2】工厂模式
【Java设计模式 - 创建型模式2】工厂模式
4 0
|
22小时前
|
设计模式 缓存 安全
【Java设计模式 - 创建型模式1】单例模式
【Java设计模式 - 创建型模式1】单例模式
4 0
|
22小时前
|
设计模式 Java
【Java设计模式 - 专栏开篇简介】 java设计模式开篇
【Java设计模式 - 专栏开篇简介】 java设计模式开篇
4 0
|
23小时前
|
设计模式 Java uml
必知的技术知识:JAVA【设计模式】命令模式
必知的技术知识:JAVA【设计模式】命令模式
|
1天前
|
设计模式 Java 机器人
Java设计模式之状态模式详解
Java设计模式之状态模式详解
|
1月前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式