单例设计模式解析
🥤概述:单例设计模式是一种创建型设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
一、饿汉式实现
🎈介绍:饿汉式是一种单例设计模式的实现方式,其核心思想是在类加载时就创建实例对象并将其静态化,之后在每次获取实例时直接返回该静态对象。因此,饿汉式实现起来相对简单,在多线程环境下也比较安全,但是如果该实例一直没有被使用,会浪费一定的系统资源。
🔔代码实现:
public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static修饰*/private static final Singleton INSTANCE = new Singleton();/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法返回该实例**/public static Singleton getInstance() {return INSTANCE;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}
😊代码解析:
这是一个单例模式的经典实现,具体解释如下:
- 将自身实例化对象设置为一个属性,并用static修饰,这样在类加载时就会创建这个实例,并且该实例在整个应用程序生命周期中只会创建一次,从而保证了单例的唯一性。同时,由于该属性被声明为private,外部无法访问,从而保证了单例的封装性。
- 将构造器私有化,这样外部就无法通过new关键字创建实例,只能通过getInstance方法获取实例。这样可以控制单例的创建过程,并保证了单例的一致性。
- 提供一个静态方法getInstance(),该方法返回该实例。由于INSTANCE属性被声明为static,所以getInstance()方法也需要声明为static,这样可以在不创建实例的情况下访问该属性,并返回该实例。同时,由于该方法是静态方法,所以可以直接通过类名来调用,从而保证了单例的简单性和易用性。
总的来说,该实现方式简单明了,且具有较好的线程安全性和性能,是单例模式的经典实现方式之一。
二、懒汉式实现
🎈介绍:懒汉式是单例模式的一种实现方式,其特点是在需要时才创建实例,而不是在类加载时就创建实例。懒汉式通常有两种实现方式:加锁的和不加锁的
🔔代码实现:
public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static修饰*/private static Singleton INSTANCE;/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法返回该实例**/public static Singleton getINSTANCE() {if (INSTANCE == null) {INSTANCE = new Singleton();}return INSTANCE;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}
😊代码解析:
这是一个单例模式的懒汉式实现,具体解释如下:
- 将自身实例化对象设置为一个属性,并用static修饰,但不在定义时进行实例化,而是在getINSTANCE()方法中通过判断是否为null来决定是否需要实例化。这样在类加载时不会创建实例,而是在第一次使用时创建,从而实现了懒加载的效果,节省了系统资源。
- 构造器被私有化,从而避免了外部通过new关键字来创建实例,只能通过getINSTANCE()方法获取实例。这样可以控制单例的创建过程,并保证了单例的一致性。
- 提供了一个静态方法getINSTANCE(),该方法返回该实例。由于INSTANCE属性被声明为static,所以getINSTANCE()方法也需要声明为static,这样可以在不创建实例的情况下访问该属性,并返回该实例。同时,由于该方法是静态方法,所以可以直接通过类名来调用,从而保证了单例的简单性和易用性。
总的来说,该实现方式实现了懒加载的效果,但由于在多线程环境下存在竞争条件,可能会导致多个线程同时创建实例,从而影响单例的唯一性。因此,在多线程环境下需要进行线程安全处理。
➡️➡️这是懒汉式的单例实现方式,与饿汉式的主要区别是:懒汉式在第一次调用 getINSTANCE()
方法时才会实例化对象,而不是在类加载时就实例化对象。
- 这种实现方式在单线程环境下没有问题,但在多线程环境下存在线程安全问题,可能会创建出多个实例。因为多个线程可能同时进入
if (INSTANCE == null)
的判断语句,导致多次实例化。 - 为了解决这个问题,可以在
getINSTANCE()
方法上加锁,或者使用双重校验锁等方式来保证线程安全。
三、双重检查锁实现
🎈介绍:双重检查锁实现单例设计模式-懒汉式,是一种更加高效、线程安全的单例实现方式。它利用了volatile关键字和synchronized关键字的特性,避免了多线程环境下创建多个实例的问题。
🔔代码实现:
public class Singleton {/*** 将自身实例化对象设置为一个属性,并用static、volatile修饰*/private static volatile Singleton singleton;//锁private final static Object MONITOR = new Object();/*** 构造方法私有化,防止外部实例化对象*/private Singleton() {}/*** 静态方法获取实例*/public static Singleton getSingleton() {if (singleton == null) {synchronized (MONITOR) {if (singleton == null) {singleton = new Singleton();}return singleton;}}return singleton;}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}
😊代码解析:
这是一个单例模式的懒汉式-双重检查锁实现,具体解释如下:
- 首先,该类为单例模式的实现类,通过保证一个类仅有一个实例来保证单例模式的实现。
- INSTANCE属性使用了volatile关键字进行修饰。volatile关键字的作用是保证可见性和禁止指令重排序。这样可以确保当一个线程修改了该属性的值之后,其他线程能够立即看到该修改,并且该属性在初始化时不会出现指令重排序的情况,从而保证了线程安全性。
- MONITOR对象被定义为一个final static的对象,并在静态代码块中进行初始化。该对象用于实现懒加载时的同步锁。由于MONITOR对象是final static的,因此只会在类被加载时初始化一次,并且该对象是线程安全的,因此可以放心使用。
- 构造方法被私有化,从而禁止外部通过new关键字来实例化该类的对象。
- getSingleton()方法为静态方法,用于获取该类的实例对象。在该方法内部,首先检查singleton对象是否为空,如果为空,则进入同步块,再次检查singleton对象是否为空,如果为空,则实例化一个Singleton对象并赋值给singleton,否则直接返回singleton对象。通过双重检查锁机制,保证了线程安全性和懒加载的效果。同时,为了避免多个线程在同一时间同时进入同步块,导致性能问题,采用了MONITOR对象来作为同步锁。
总的来说,该实现方式采用了双重检查锁机制来保证线程安全性和懒加载效果,同时也兼顾了性能问题。但是,需要注意的是,该方式并不是完美的,可能存在一些潜在的问题,如可能会受到指令重排序等问题的影响,需要开发者根据具体情况进行实际应用。
四、静态内部类实现
🎈介绍:
- 静态内部类是一种嵌套在外部类中的类,它的特点是只有在第一次使用时才会被加载和初始化,且它的初始化过程是线程安全的。基于这个特点,可以使用静态内部类来实现单例模式。
- 具体实现方法是将单例对象的实例化放到静态内部类中,在外部类中提供一个公共的静态方法,用于获取该单例对象的实例。由于静态内部类只有在第一次使用时才会被加载,所以这种方法可以实现懒加载,同时也保证了线程安全性和单例唯一性。
🔔代码实现:
public class Singleton {/*** 构造器私有化,防止外部实例化*/private Singleton() {}/*** 返回单例实例的静态方法*/public static Singleton getInstance() {return SingletonHolder.INSTANCE;}/*** 写一个静态内部类,里面实例化外部类*/private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(singleton.enum_.Singleton.getInstance() == singleton.enum_.Singleton.getInstance());}
}
😊代码解析:
这是静态内部类实现单例设计模式的经典写法。具体解释如下:
- 外部类 Singleton 的构造方法被私有化,这样外部无法通过 new 关键字创建实例,只能通过类的静态方法 getInstance() 获取单例对象。
- 内部类 SingletonHolder 被声明为私有的静态类,只有 Singleton 类能够访问它,从而保证了单例的封装性和安全性。
- 内部类 SingletonHolder 包含一个静态常量 INSTANCE,它被初始化为一个 Singleton 类的实例,利用类加载器保证了单例的线程安全性。
- 对外提供公共的静态方法 getInstance(),该方法返回 SingletonHolder.INSTANCE,实现了懒加载和线程安全。
总的来说,这种实现方式既保证了线程安全性,又实现了懒加载,同时代码量也比较简洁,是静态内部类实现单例设计模式的经典写法。
五、枚举实现
🎈介绍:
-
使用枚举类实现单例模式是一种简单、安全、易于实现的方式,因为枚举类本身就保证了单例的唯一性,同时也避免了其他单例模式中可能遇到的反射攻击和序列化问题。
-
在使用枚举类实现单例模式时,只需要声明一个枚举类型,并在其中定义一个实例,这个实例就是该单例模式的唯一实例,而且该实例是由枚举类在加载时自动创建的,因此无需考虑线程安全等问题。同时,由于枚举类是final类型的,不允许被继承,也不允许通过反射来创建实例,因此可以很好地避免反射攻击和序列化问题。
🔔代码实现:
public class Singleton {/*** 构造器私有化,防止外部实例化*/private Singleton() {}/*** 返回单例实例的静态方法*/public static Singleton getInstance() {return SingletonHolder.INSTANT.instance;}/*** 静态内部枚举类,实例化单例对象*/private enum SingletonHolder {INSTANT;private final Singleton instance;/*** 实例化单例对象*/SingletonHolder() {instance = new Singleton();}}/*** 测试单例是否唯一*/public static void main(String[] args) {System.out.println(Singleton.getInstance() == Singleton.getInstance());}
}
😊代码解析:
这段代码是通过枚举类实现单例模式。具体解释如下:
- 构造方法私有化,这样外部无法通过new关键字来创建该类的实例。
- 定义一个静态的getInstance()方法,返回单例实例,通过访问SingletonHolder.INSTANT.instance来获取。
- 内部定义一个枚举类SingletonHolder,在该枚举类中定义一个单例的实例instance,并在枚举类的构造方法中实例化该实例。由于枚举类中的成员变量都是static final类型的,因此保证了单例实例的唯一性和不可变性。
- 在getInstance()方法中,直接返回SingletonHolder.INSTANT.instance即可获取单例实例。
总的来说,枚举类实现单例模式的代码比较简洁,而且线程安全性和序列化安全性都能得到保障,因此在实际开发中也比较常用。