重学 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 单例模式的实现及其优缺点。在实际应用中,我们需要根据具体场景灵活地选择不同的单例模式,结合业务特点来设计实现。希望本文对您有所帮助,谢谢!

目录
相关文章
|
23天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
32 2
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
64 2
|
2月前
|
设计模式 安全 Java
Java编程中的单例模式深入剖析
【10月更文挑战第21天】在Java的世界里,单例模式是设计模式中一个常见而又强大的存在。它确保了一个类只有一个实例,并提供一个全局访问点。本文将深入探讨如何正确实现单例模式,包括常见的实现方式、优缺点分析以及最佳实践,同时也会通过实际代码示例来加深理解。无论你是Java新手还是资深开发者,这篇文章都将为你提供宝贵的见解和技巧。
96 65
|
11天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
13天前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
27 4
|
22天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
23天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
5天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
19 1
|
13天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
22 0