在面向对象编程中,单例指的是一个类只允许创建一个实例,并且提供一个全局访问的接口。单例模式主要解决的问题是在多线程环境下,如何保证一个类只有一个实例,并且能够在全局范围内访问该实例。懒汉式单例模式是其中一种最为常见的实现方式。
一、基本定义
懒汉式单例模式也称为“懒加载”或“延迟初始化”,其主要思想是:只有当第一次请求实例时,才会实例化对象,避免了类在初始化时就实例化对象,从而影响应用的启动效率。
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
在上面的代码中,instance是Singleton类的一个私有静态变量,用于存储该类的唯一实例。getInstance()方法是获取该实例的唯一接口。在每次调用getInstance()方法时,都会先判断instance是否为空,如果为空,则创建一个新的实例,否则直接返回已存在的实例。
二、线程安全
由于多线程环境下有可能出现竞态条件,从而导致两个线程同时执行到instance == null的判定语句,从而实例化两个实例,所以懒汉式单例模式的线程安全性较差。
为了避免这种问题,可以采用多种方式对getInstance()方法进行线程安全的设计。下面介绍三种常用的线程安全方案:
1. synchronized关键字
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
通过在getInstance()方法上添加synchronized关键字,可以将该方法变为同步方法,从而保证了只有一个线程能够同时进入该方法进行实例化。但是由于每次调用该方法都需要获得锁,所以会造成较大的性能损耗。
2. Double Check Lock(双重校验锁)
public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
在Double Check Lock方案中,同样是通过使用synchronized关键字对instance的实例化进行加锁,但是只有在instance为null时,才会进入synchronized块进行实例化。这种方式既保证了线程安全性,又可以避免多次获取锁造成的性能损耗。
3. 静态内部类
public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
在静态内部类方案中,Singleton类中的instance被声明为私有,并且新建了一个静态内部类SingletonHolder,在 SingletonHolder类中声明了一个静态的、final的、并且是Singleton类型的变量INSTANCE。在getInstance()方法中,直接返回SingletonHolder.INSTANCE,从而保证了线程安全性,同时也可以达到懒加载的效果。
三、序列化与反序列化
在使用单例模式时,有时需要对单例对象进行序列化或反序列化,但是由于单例类的构造函数被私有化,并且getInstance()方法是静态方法,所以需要对Singleton类进行特殊处理才能保证序列化的正确性。以下是一种序列化与反序列化的方式:
public class Singleton implements Serializable { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } private Object readResolve() throws ObjectStreamException { return instance; } }
在上述代码中,我们将instance变量声明为静态变量,并在定义时就实例化了对象。为了保证反序列化时不会创建新的实例,我们定义了一个readResolve()方法,将反序列化返回的对象直接指向instance,从而保证了单例的正确性。
四、反射
在Java的反射机制中,可以通过调用构造函数的newInstance()方法来创建一个类的实例。对于懒汉式单例模式而言,如果想要通过反射来创建新的实例,则需要对代码进行特殊的设计。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { if (instance != null) { throw new IllegalStateException("Already initialized."); } } public static Singleton getInstance() { return instance; } private Object readResolve() throws ObjectStreamException { return instance; } }
在上面的代码中,我们在Singleton类的私有构造函数中加入了一个判断语句,判断instance是否为null。如果instance已经被实例化,则抛出IllegalStateException异常,从而避免通过反射创建新的实例。
五、结论
懒汉式单例模式是一种实现单例模式的常见方式,其主要思想是在需要时才进行实例化。但是由于多线程环境下可能存在线程安全问题,需要进行特殊的处理。此外,序列化与反序列化以及反射都可能对懒汉式单例模式带来一些问题,需要进行特殊的设计和处理。
原创文章,作者:UNVTW,如若转载,请注明出处:https://www.506064.com/n/372541.html