在实际开发中为了节省系统资源,有时需要保证系统中某个实例只有一个实例,而这种模式就是我们常见的单例模式。单例模式一般有三个关键点:
实例模式相比其他模式实现起来相对简单。比如现在我们的系统需要创建一个独特的文件管理器(FileManager):
公共 类 文件管理器 {
私有 静态 文件管理器实例;
公共 文件管理器() {
}
公共 静态 文件管理器getInstance (){
if(实例 == null){
实例 = 新 FileManager();
}
返回 实例;
}
公共 静态 void main(字符串[] args) {
FileManager fileManager1 = FileManager.getInstance();
FileManager fileManager2 = FileManager.getInstance();
System.out.println(fileManager1 == fileManager2);
}
}
通过上面的代码我们很简单的就实现了单例模式,从运行结果来看可以发现fileManager1
和fileManager2
就是同一个实例。
上面的代码看似没有问题,但实际上还是有很大的问题。上面的代码在单线程的情况下不会有问题,但在多线程的情况下就不一定了。多线程的情况下,很可能多个线程在判断instance == null
时会同时判断true
。这将导致多个线程进入实例。 = new FileManager()
,导致单例模式失败。那么出现这种情况该如何解决呢?
公共 静态 同步 FileManager getInstanceWithSyncLock (){
if(实例== 空){
因为 返回 实例;
}
这是通过添加同步锁来保证线程安全的最简单的方法,但是这种方法的问题是会有性能的损失,在实际开发中并不是最好的解决方案。
此前,多线程下使用同步的方式来解决单例线程安全问题,但出于性能原因并不推荐使用。事实上,同步方法的问题在于每次获取实例时都需要添加同步锁。即使实例已经成功创建,仍然需要使用同步锁,这会导致性能不佳。我们可以通过使用双重检查锁定来很好地解决这个问题。修改代码如下:
公共 静态 文件管理器 getInstanceWithDCL(){
,而此时的线程安全是由虚拟机保证的。它保证了成员变量只能初始化一次,整个过程不需要同步锁,保证线程安全。但这种方法对编程语言有要求。在java中我们可以这样设计,但对于其他语言则不一定如此。
if(实例 == null){
已同步(文件管理器。类){
|不过,需要注意的一点是,对于
实例
实例,我们需要使用易失性
来修改它,以避免指令排序的负面影响。使用双锁可以解决实例创建后无锁获取单个实例的问题,也可以保证线程安全。与之前的方法相比,性能有所提高。饿汉式单身汉
我们上面实现的单例模式通常被称为惰性单例。它的特点是只有当我们获得单例时才会触发单例的实例化。如果我们从不调用它,系统将不会创建实例。 。这种惰性单例的负面影响就是存在线程安全问题,需要使用同步锁来解决安全问题。与懒惰式单例相反的是饥饿式单例。它的特点是,无论你使用与否,它都已被创建。
公共 类 单例 {
私有 静态 单例实例 = 新 单例 (); | | main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance(); }从代码实现可以看出,这种方法不存在线程安全问题,而且代码实现也非常简单。但这种方法的缺点是,即使你整个系统没有人获得实例,这个实例仍然会被创建。与懒惰单例相比,饥饿风格会占用更多的系统资源。
初始化需求支架
之前介绍的懒惰式和饥饿式单例各有优缺点。有没有办法同时实现无锁和延迟加载?答案是有点,这个方法我们通常称之为初始化需求持有者(IoDH)。我们直接看代码看看是如何实现的。代码如下:
公共 类 IoDHS单例 {
私有 IoDHSingleton(){}
私有 静态 类 HolderClass{
私人 静态 final IoDHSingleton 实例 = new IoDHSingleton();
}
public 静态 IoDHS单例getInstance() {
返回 HolderClass.instance;
}
public 静态 空白 主要(String[] args) {
IoDHSingleton instance1 = IoDHSingleton.getInstance();
IoDHSingleton instance2 = IoDHSingleton.getInstance();
System.out.println(instance1 ==实例2);
}
}通过代码可以发现,该方法在单例类内部添加了一个静态内部类,在内部类中创建了一个单例对象,然后通过
getInstance
将单例对象返回给外部方法使用。由于静态单例对象没有实例化为IoDHSingleton
的成员变量,因此在类加载时不会实例化该对象,以达到延迟加载的效果。当第一次调用getInstance()
方法时,会加载内部类HolderClass
。这时,HolderClass
中的成员变量就会被创建。 实例总结
单例模式是设计模式中最好理解的设计模式,而且这种模式在实际开发中使用得也非常频繁。单例模式的核心是提供一个唯一的实例供整个系统使用。只要我们能够保证系统中获取到的实例是同一个实例,我们就可以称之为单例模式。
本文示例代码地址:
https://Gitwww.sychzs.cn/zengchao_workspace/design-pattern