博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单例模式
阅读量:6991 次
发布时间:2019-06-27

本文共 2255 字,大约阅读时间需要 7 分钟。

  • 定义

单例模式确保一个类只有一个实例,并提供一个全局访问点

  • 解释

从定义可以看出,特点是这个类只有一个实例。那么,为什么要这么做呢?原因在于,有些时候,这个类只有一个实例会节约资源,或者只有一个实例才能保证整个程序运行正确,一致。例如:线程池,缓存,对话框,日志对象等等 。

  • 示例
class Singleton {
private static Singleton singleton; private Singleton() {
} public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton(); } return singleton; }}

这是单例的经典使用方式:

  • 一个 private static 对象
  • 构造器设置为 private
  • 一个 public static 方法提供全局访问点

开始的时候,其实我比较困惑为什么不在 singleton 声明处直接实例化对象,后来明白了,这是一种延迟实例化的手段,保证只在需要时才实例化。如果直接在声明时实例化,那么只要类加载了,即使不需要对象,也会对它进行实例化。

另外,这个经典使用方式其实是有问题的,对比后面 Tomcat 中的应用场景,你可能会发现问题所在。

在 Tomcat 中,就有一个单例模式,它是 org.apache.catalina.tribes.util.StringManager 类。在 Tomcat 中,会有许多地方需要对错误消息进行处理。我们使用 StringManager 类来管理这些错误消息。

错误消息首先不能硬编码到代码中,否则需要提供国际化支持时,就会很痛苦。错误消息需要定义在配置文件中。Tomcat 为每个包 (package) 都提供了三种语言的错误消息配置文件。每个包内都有很多类,我们没必要为每个类都生成一个 StringManager类,因为它们共享同一个配置文件。所以,一个包只需要一个 StringManager 对象就好了。我们怎么为一个包生成一个StringManager 呢? Tomcat 在 StringManager 内部保存着所有包的 StringManager 实例,你需要一个实例时,只需要提供包名,调用 StringManager 的相应方法,就会返回与此包名对应的 StringManager 实例。下面是相关的代码,一目了然。

public class StringManager {
private StringManager(String packageName) {
...}private static final Hashtable
managers =new Hashtable<>();/*** Get the StringManager for a particular package. If a manager for* a package already exists, it will be reused, else a new* StringManager will be created and returned.** @param packageName The package name*/public static final synchronized StringManager getManager(String packageName) {
StringManager mgr = managers.get(packageName); if (mgr == null) {
mgr = new StringManager(packageName); managers.put(packageName, mgr); } return mgr;}

使用了一个 Hashtable 来保存 managers,每次通过 getManager 方法,通过包名访问,如果访问不到,就为此包新生成一个StringManager 实例。

有没有注意到 getManager 方法被 synchronized 修饰?这就是之前我们举的经典示例时说的问题。在使用单例时,只有一个对象,这个对象可能是被多个线程共享的。如果不同步,就可能会出现数据不一致的情况。例如,两个线程同时调用了getManager,访问同一个包名。正确的执行是,其中一个线程第一次调用时,mgr 为空,此时生成一个 StringManager。第二个线程调用时,mgr 就不为空了。但是,如果不同步,那么当第一个线程通过了 if(mgr == null) 时,此时线程被切换了,这时,第二个线程也会通过 if(mgr == null) ,这样就导致同一个包,生成了两个 StringManager

  • 扩展阅读

关于单例模式与线程安全,建议阅读一下这篇文章:

关于单例模式的其它实现方式,可以阅读

转载于:https://www.cnblogs.com/Iambda/p/3933451.html

你可能感兴趣的文章
angular4学习记录 -- 组件通讯、生命周期
查看>>
标准模式与混杂模式
查看>>
<<编写可维护的javascript>> 笔记7(事件处理)
查看>>
关于javascript中的bind、call、apply等函数的用法
查看>>
自己动手实现一个简单的JSON解析器
查看>>
GitChat · 前端 | React 生态系统:从小白到牛人
查看>>
浏览器中的ES6模块懒加载
查看>>
分享一个基于vue的环形菜单组件
查看>>
[译]WebAssembly 中的 Memory
查看>>
php 获取 ip 信息
查看>>
从redux-thunk到redux-saga实践
查看>>
【译】提高nginx9倍性能的线程池
查看>>
react进阶系列:高阶组件详解(二)
查看>>
mongodb 学习记录
查看>>
socket.io不为人知的功能
查看>>
[转]phpstorm工具的使用收集
查看>>
js基础 正则
查看>>
执行环境和作用域
查看>>
为什么不要在枚举和 Equatable 中使用 default case?
查看>>
php实现排序(选择,冒泡,快速)
查看>>