一文带你理解单例模式

定义

确保某一个类只有一个实例,并且自行实例化向整个系统提供该实例

场景

整个系统只需要一个全局的实例对象。在衡量对象资源占用时,比如IO、数据库访问等,不希望出现多个实例化对象的时候

关键点

  1. 构造函数私有化
  2. 通过一个静态方法或者枚举返回单例类对象
  3. 确保单例类对象有且只有一个,特别是在多线程的环境下
  4. 确保单例对象在反序列化时不会重新构建对象

以上四点中,第四点不是必须的,只在涉及反序列化的系统中有要求,下文设计的说明中,该关键点不参与优劣的评判。

创建方式

饿汉模式

在类的初始化阶段就创建单例类对象

public class Singleton {

  private static Singleton sInstance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return sInstance;
  }
}

优点:轻松的解决了多线程问题。(类的初始化操作由虚拟机确保是同步的)

缺点:一开始就创建了类对象,造成内存空间的浪费

说明:这种方式一般不建议使用

懒汉模式

在首次获取单例类对象时创建单例类对象

public class Singleton {

  private static Singleton sInstance;

  private Singleton() {
  }

  public static Singleton getInstance() {
    synchronized (Singleton.class) {
      if (sInstance == null) {
        sInstance = new Singleton();
      }
    }
    return sInstance;
  }
}

优点:需要时才创建对象,节省内存开销

缺点:每次获取单例类对象时,都会对代码进行同步锁处理,消耗不必要的系统资源

说明:这种方式同样不建议使用

DCL模式

双重校验锁模式。类似懒汉模式,在首次获取时创建单例对象,针对懒汉模式的优化

public class Singleton {

  private static Singleton sInstance;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (sInstance == null) {
      synchronized (Singleton.class) {
        if (sInstance == null) {
          sInstance = new Singleton();
        }
      }
    }
    return sInstance;
  }
}

优点:需要时才创建对象,节省内存开销,外层非加锁判空操作,避免同步锁的额外开销

缺点:外层判空在某些情况下,不可靠,针对多线程可能存在风险

说明:需要借助volatile关键字来协助解决外层不可靠的问题。在JDK1.6(不包含)以下时,volatile关键字不能妥善的处理变量的可见性。在JDK1.6及以上版本,volatile关键字已经解决了该问题。
这种方式也是目前使用比较多的一种。

静态内部类

通过在单例类中创建一个私有的静态容器类,该容器类中定义静态常量并初始化,类型为单例类的类型。在每次获取单例类对象时,直接返回内静态内部类的静态成员变量即可。

public class Singleton {

  private Singleton() {
  }

  public static Singleton getInstance() {
    return SingletonHolder.instance;
  }

  private static class SingletonHolder {
    private static Singleton instance = new Singleton();
  }
}

优点:很好的解决了多线程问题。不会在类初始化阶段创建对象。

缺点:需要创建额外的静态内部类。

说明:这种方式优势比较明显,而且没有明显的劣势。也是部分IDE在自动化生成单例类是采用的方式。推荐使用这种方式。

通过枚举类实现

借助枚举实例的创建是线程安全的,并且只会实例化一次的特性来设计的。而且由于枚举类的特殊性,保证了反序列化时不会生成新的实例对象

public enum SingletonEnum {
  INSTANCE;

  public int sum(int x, int y) {
    return x + y;
  }
}

优点:多线程安全、保证真实的单例(枚举特性),书写简单

缺点:枚举类区别于普通的类,在接口设计和交互层有明显的障碍

说明:这种方式优势非常明显,劣势也非常明显。基于谷歌的说法,凡是使用枚举的地方,都可以通过静态常量来解决。

通过容器来实现

通过容器类来实现属于另辟蹊径。借助一些稳定的工具类来实现,比如说Map,Set等,可以看下面代码块

public class Singleton {

  private static Map<String, Object> container = new HashMap();

  private Singleton() {
  }

  public static Object getInstance(String key) {
    return container.get(key);
  }

  public static void addInstance(String key, Object obj) {
    if (!container.containsKey(key)) {
      container.put(key, obj);
    }
  }
}

优点:可以实现多个类的单例模式

缺点:借助额外的工具类,使用场景比较局限

说明:通过管理类容器,可以实现多个类的单例对象获取。这种方式在特定的环境下会起到很好的作用。

涉及到的问题

根据单例类的特性,一旦单例对象被创建,因为该实例是静态的,到GC-ROOT具有应用周期内的可达性。因此在单例模式下,需要谨慎的处理好单例对象的引用关系,防止内存泄漏

总结

综合考量对资源的开销、实现复杂度等因素,采用静态内部类的形式来设计单例类是目前被推崇的方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统是计算机系统中的核心组成部分,负责管理和协调计算机硬件和软件资源,提供程序运行环境。在CSDN上有很多关于操作系统的专题文章,以下将从操作系统的基本概念、功能和常见类型等方面简要介绍一下。 首先是操作系统的基本概念。操作系统是一种系统软件,它是计算机硬件和应用软件之间的桥梁,提供给应用程序一系列的服务和资源,同负责调度和管理系统资源。它为用户屏蔽了底层的硬件差异,提供了一个统一的、易于使用的界面。 操作系统主要有四个基本功能。首先是处理器管理,负责将处理器分配给系统中的各个进程,并进行进程切换,实现多道程序并发执行。其次是内存管理,管理计算机的内存资源,包括分配、回收和保护等操作。再次是文件管理,负责管理文件的存储、命名和保护等操作,提供了文件操作的接口。最后是设备管理,负责管理计算机的各种设备,包括输入输出设备和存储设备等。 常见的操作系统有多种类型。最主流的是Windows、Linux和Mac OS等桌面操作系统。此外还有服务器操作系统,如Windows Server和Linux等,用于管理和部署服务器。还有嵌入式操作系统,如Android和iOS等,用于移动设备和物联网设备。操作系统也有实操作系统,用于需要实控制和响应的系统,如工控系统和航空航天系统等。 总之,操作系统是计算机系统中不可或缺的重要组成部分,通过CSDN上的相关文章,我们可以更深入了解操作系统的基本概念、功能和不同类型。这些知识对于理解计算机系统的工作原理和提升编程能力都有着重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值