Java Singleton设计模式最佳实践与示例

Java Singleton Pattern四种帮派设计模式之一,属于创建设计模式类别。从定义来看,它似乎是一个非常简单的设计模式,但是当涉及到实现时,它会带来很多实现问题。Java Singleton模式的实现一直是开发人员争议的话题。在这里,我们将了解Singleton设计模式原理,实现Singleton设计模式的不同方法以及一些使用它们的最佳实践。

Java Singleton

  • Singleton模式限制类的实例化,并确保java虚拟机中只存在该类的一个实例。
  • 单例类必须提供一个全局访问点来获取类的实例。
  • 单例模式用于日志记录,驱动程序对象,缓存和线程池。
  • Singleton设计模式也用于其他设计模式,如Abstract Factory,Builder,Prototype,Facade等。
  • 例如java.lang.Runtime,单核设计模式也用于核心java类中java.awt.Desktop

Java Singleton模式

为了实现Singleton模式,我们有不同的方法,但它们都有以下常见概念。

  • 私有构造函数,用于限制其他类的实例化。
  • 同一个类的私有静态变量,它是该类的唯一实例。
  • 返回类实例的公共静态方法,这是外部世界获取单例类实例的全局访问点。

在后面的部分中,我们将学习Singleton模式实现的不同方法以及设计实现的关注点。

  1. 急切的初始化
  2. 静态块初始化
  3. 延迟初始化
  4. 线程安全单例
  5. Bill Pugh Singleton实施
  6. 使用Reflection来销毁Singleton Pattern
  7. Enum Singleton
  8. 序列化和单身人士

急切的初始化

在急切的初始化中,Singleton类的实例是在类加载时创建的,这是创建单例类的最简单方法,但它有一个缺点,即即使客户端应用程序可能没有使用它,也会创建实例。

这是静态初始化单例类的实现。


package com.journaldev.singleton;public class EagerInitializedSingleton {private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();//private constructor to avoid client applications to use constructorprivate EagerInitializedSingleton(){}public static EagerInitializedSingleton getInstance(){return instance;}
}

如果您的单例类没有使用大量资源,那么这就是使用方法。但是在大多数场景中,Singleton类是为文件系统,数据库连接等资源创建的。除非客户端调用该getInstance方法,否则我们应该避免实例化。此外,此方法不提供任何异常处理选项。

静态块初始化

静态块初始化实现类似于急切初始化,除了在静态块中创建类的实例,该静态块提供异常处理选项。


package com.journaldev.singleton;public class StaticBlockSingleton {private static StaticBlockSingleton instance;private StaticBlockSingleton(){}//static block initialization for exception handlingstatic{try{instance = new StaticBlockSingleton();}catch(Exception e){throw new RuntimeException("Exception occured in creating singleton instance");}}public static StaticBlockSingleton getInstance(){return instance;}
}

急切初始化和静态块初始化都会在使用之前创建实例,这不是最佳实践。因此,在后面的部分中,我们将学习如何创建支持延迟初始化的Singleton类。

阅读:Java静态

延迟初始化

实现Singleton模式的延迟初始化方法在全局访问方法中创建实例。以下是使用此方法创建Singleton类的示例代码。


package com.journaldev.singleton;public class LazyInitializedSingleton {private static LazyInitializedSingleton instance;private LazyInitializedSingleton(){}public static LazyInitializedSingleton getInstance(){if(instance == null){instance = new LazyInitializedSingleton();}return instance;}
}

上述实现在单线程环境中可以正常工作,但是当涉及到多线程系统时,如果多个线程同时位于if条件内,则会导致问题。它将破坏单例模式,两个线程将获得单例类的不同实例。在下一节中,我们将看到创建线程安全单例类的不同方法。

线程安全单例

创建线程安全的单例类的更简单方法是使全局访问方法同步,以便一次只有一个线程可以执行此方法。这种方法的一般实现类似于下面的类。


package com.journaldev.singleton;public class ThreadSafeSingleton {private static ThreadSafeSingleton instance;private ThreadSafeSingleton(){}public static synchronized ThreadSafeSingleton getInstance(){if(instance == null){instance = new ThreadSafeSingleton();}return instance;}}

上面的实现工作正常并提供了线程安全性,但由于与synchronized方法相关的成本,它降低了性能,尽管我们只需要可能创建单独实例的前几个线程(读取:Java同步)。为了避免每次额外的开销,使用双重检查锁定原理。在此方法中,synchronized块在if条件内使用,并进行额外检查以确保仅创建单个类的一个实例。

下面的代码片段提供了双重检查的锁定实现。


public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){if(instance == null){synchronized (ThreadSafeSingleton.class) {if(instance == null){instance = new ThreadSafeSingleton();}}}return instance;
}

阅读:线程安全单例类

Bill Pugh Singleton实施

在Java 5之前,java内存模型存在很多问题,并且上述方法在某些情况下失败,在这些情况下,太多线程试图同时获取Singleton类的实例。因此Bill Pugh提出了一种使用内部静态助手类创建Singleton类的不同方法。Bill Pugh Singleton的实施就是这样的;


package com.journaldev.singleton;public class BillPughSingleton {private BillPughSingleton(){}private static class SingletonHelper{private static final BillPughSingleton INSTANCE = new BillPughSingleton();}public static BillPughSingleton getInstance(){return SingletonHelper.INSTANCE;}
}

请注意包含singleton类实例的私有内部静态类。加载单例类时,SingletonHelper类不会加载到内存中,只有当有人调用getInstance方法时,才会加载此类并创建Singleton类实例。

这是Singleton类最广泛使用的方法,因为它不需要同步。我在我的许多项目中使用这种方法,它也很容易理解和实现。

阅读:Java嵌套类

使用Reflection来销毁Singleton Pattern

反射可用于销毁所有上述单例实现方法。让我们看一下示例类。


package com.journaldev.singleton;import java.lang.reflect.Constructor;public class ReflectionSingletonTest {public static void main(String[] args) {EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance();EagerInitializedSingleton instanceTwo = null;try {Constructor[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors();for (Constructor constructor : constructors) {//Below code will destroy the singleton patternconstructor.setAccessible(true);instanceTwo = (EagerInitializedSingleton) constructor.newInstance();break;}} catch (Exception e) {e.printStackTrace();}System.out.println(instanceOne.hashCode());System.out.println(instanceTwo.hashCode());}}

当您运行上面的测试类时,您会注意到两个实例的hashCode与销毁单例模式的方法不同。反射是非常强大的,并且在很多框架中使用,比如Spring和Hibernate,请查看Java Reflection Tutorial

Enum Singleton

为了克服这种情况,Joshua Bloch建议使用Enum来实现Singleton设计模式,因为Java确保任何枚举值只在Java程序中实例化一次。由于Java Enum值是全局可访问的,因此单例也是如此。缺点是枚举类型有些不灵活; 例如,它不允许延迟初始化。


package com.journaldev.singleton;public enum EnumSingleton {INSTANCE;public static void doSomething(){//do something}
}

阅读:Java Enum

序列化和单身人士

有时在分布式系统中,我们需要在Singleton类中实现Serializable接口,以便我们可以将其状态存储在文件系统中并在以后的时间点检索它。这是一个小型单例类,它也实现了Serializable接口。


package com.journaldev.singleton;import java.io.Serializable;public class SerializedSingleton implements Serializable{private static final long serialVersionUID = -7604766932017737115L;private SerializedSingleton(){}private static class SingletonHelper{private static final SerializedSingleton instance = new SerializedSingleton();}public static SerializedSingleton getInstance(){return SingletonHelper.instance;}}

序列化单例类的问题在于,每当我们反序列化它时,它将创建该类的新实例。让我们用一个简单的程序来看。


package com.journaldev.singleton;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;public class SingletonSerializedTest {public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {SerializedSingleton instanceOne = SerializedSingleton.getInstance();ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));out.writeObject(instanceOne);out.close();//deserailize from file to objectObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();in.close();System.out.println("instanceOne hashCode="+instanceOne.hashCode());System.out.println("instanceTwo hashCode="+instanceTwo.hashCode());}}

上述程序的输出是;


instanceOne hashCode=2011117821
instanceTwo hashCode=109647522

所以它破坏了单例模式,为了克服这种情况我们需要做的就是提供readResolve()方法的实现。


protected Object readResolve() {return getInstance();
}

在此之后,您会注意到两个实例的hashCode在测试程序中是相同的。

阅读:Java序列化和Java反序列化。

我希望这篇文章可以帮助您掌握Singleton设计模式的精细细节,通过您的想法和评论让我知道。

转载来源:https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples

创建设计模式 - Singleton设计模式(最佳实践与示例)相关推荐

  1. singleton设计模式_Java Singleton设计模式最佳实践与示例

    singleton设计模式 Java Singleton Pattern is one of the Gangs of Four Design patterns and comes in the Cr ...

  2. 反射和内省_单例设计模式–内省和最佳实践

    反射和内省 定义: Singleton是" 四人帮"设计模式的一部分,它属于创新设计模式. 在本文中,我们将更深入地研究Singleton模式的用法. 就建模而言,它是最简单的设计 ...

  3. 单例设计模式–内省和最佳实践

    定义: Singleton是" 四人帮"设计模式的一部分,它属于创新设计模式. 在本文中,我们将更深入地研究Singleton模式的用法. 就建模而言,它是最简单的设计模式之一,但 ...

  4. 在SQL Server中配置索引创建内存设置的最佳实践

    介绍 (Introduction) The Index Create Memory setting is an advanced SQL Server configuration setting wh ...

  5. Java延迟加载的最佳实践应用示例!

    作者 | S.L 来源 | http://r6d.cn/abGzy 代码中的很多操作都是Eager的,比如在发生方法调用的时候,参数会立即被求值.总体而言,使用Eager方式让编码本身更加简单,然而使 ...

  6. java 批量处理 示例_Java异常处理教程(包含示例和最佳实践)

    java 批量处理 示例 异常是可能在程序执行期间发生的错误事件,它会破坏其正常流程. Java提供了一种健壮且面向对象的方式来处理异常情况,称为Java异常处理 . 我们将在本教程中研究以下主题. ...

  7. Java异常处理教程(包含示例和最佳实践)

    异常是可能在程序执行期间发生的错误事件,它会破坏其正常流程. Java提供了一种健壮且面向对象的方式来处理异常情况,称为Java异常处理 . 我们将在本教程中研究以下主题. Java异常处理概述 异常 ...

  8. 玩转ECS第8讲 | 服务器迁移中心SMC最佳实践及新特性介绍

    简介:本次分享由阿里云技术专家白辉万(百宝)为大家介绍免费的服务器迁移上云最佳实践方案和新功能特性,包括一键迁云.自动定期同步.一键验证.本次分享内容将帮助企业上云客户越过高高的服务器迁移门槛,快速体 ...

  9. k8s停止服务_Kubernetes 服务部署最佳实践(二) 如何提高服务可用性

    引言 上一篇 文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨. 怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并 ...

最新文章

  1. yolov5训练自己的数据
  2. 50个python库
  3. mysql 中 add2_计算器中的F,4,2,0,ADD2怎么调,MU键有什么用??急急急
  4. uva 1331——Minimax Triangulation
  5. 如果面试官问你:Redis 内存满了怎么办?
  6. c语言 amp a 1,c语言那些细节之a+1和a+1的区别
  7. ImageMagick---import(截图)
  8. oracle clob 粘贴,使用Oracle SQL Developer将CLOB导出到文本文件
  9. 手把手教如何用bib文件在latex里引用文献
  10. CAA-几何图形集下直接添加参数
  11. Deepin与Debian的对应关系
  12. 史上最简单的Linux内核IIO子系统入门demo_内核版本4.4.194
  13. 评估酒店营销效果的12个重要KPI指标
  14. 毕业一年有感——人活着的意义是什么?
  15. 64匹马,8个赛道,找出跑得最快的4匹马,至少比赛几场?
  16. 《麦肯锡方法》第6章 层级管理-思维导图
  17. 留言赠书|AI圣经,这本书YYDS
  18. Java开发面试题!mysqlfront导出数据库
  19. iOS审核上架【万能指南】
  20. 人工智能真正的意义?

热门文章

  1. Magento教程 5:系统安装与备份
  2. PHP中的CURL函数库
  3. 背包——完全背包Warcraft III(哈理工1053)
  4. zabbix3.4.4 监控系统安装部署
  5. linux压缩和解压缩命令汇总
  6. 【Latex】分数写法区别
  7. 加载本地json文件,并利用批处理调用Chrome显示html
  8. jdbc工具类 配置版本 20210412_222527.mp4
  9. 小货车DataAdapter对象 1129
  10. java类与对象 演练 查找并修改姓名