设计模式概述与 Singleton 模式详解
在软件设计中,设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。
设计模式可以分为三大类:创建型、结构型和行为型。下面我们将深入探讨创建型模式中的 Singleton 模式。
Singleton 模式
Singleton 模式的意图是确保一个类只有一个实例,并提供该实例的全局访问点。这个模式的关键在于确保在整个应用程序中只有一个实例,并且提供了一个全局访问点来访问该实例。
实现 Singleton 模式
实现 Singleton 模式有多种方式,我们将介绍四种常见的实现方式。
### 1. 懒汉式(线程不安全)
懒汉式的实现方式是延迟实例化的,这样做的好处是,如果没有用到该类,那么就不会实例化该类,从而节约资源。但是这种实现方式在多线程环境下是不安全的,因为多个线程能够同时进入 if (uniqueInstance == null) 语句块,并且此时 uniqueInstance 为 null,那么会有多个线程执行 uniqueInstance = new Singleton(); 语句,这将导致实例化多次 uniqueInstance。
### 2. 饿汉式(线程安全)
饿汉式的实现方式是直接实例化 uniqueInstance,这样可以避免线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
### 3. 懒汉式(线程安全)
懒汉式的实现方式可以通过加锁来解决线程不安全问题。只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。
### 4. 双重校验锁(线程安全)
双重校验锁的实现方式可以解决线程不安全问题,并且也能避免延迟实例化的缺点。uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
在上面的实现中,我们使用 volatile 关键字修饰 uniqueInstance,以确保多个线程能够正确地访问 uniqueInstance。使用 volatile 关键字可以避免指令重排序的问题,使得 uniqueInstance 的初始化操作能够正确地执行。
设计模式是软件设计中非常重要的一部分,学习和掌握设计模式可以帮助我们更好地设计和实现软件系统。Singleton 模式是创建型模式中的一种,通过学习 Singleton 模式,我们可以更好地理解创建型模式的设计思想和实现方式。