编程之路

  • home
  • article
  • class
  • label
  • utils

  • 搜索
Elasticsearch MongoDB 衡量点 aop 边缘计算 框架 物联网 敏捷开发 团队 压力测试 Markdown 学习方法 学习 产品 规范 日志 微服务 壁纸 开发平台 Java 全栈 前端 开发规范 MQTT CentOS 镜像站 IntelliJ IDEA FreeMarker UML 计算机 软件 Tomcat Netty Web Service Docker Dubbo Kafka NoSQL Redis 消息队列 RocketMQ RabbitMQ ActiveMQ 分布式事务 Spring 队列 Java 高级 GC JVM HTTP 网络安全 算法 设计模式 Spring Cloud Web SpringMVC 线程池 并发 锁🔒 多线程 Git Java 集合 Java 基础 MyBatis 数据库 MySQL Java 基础面试题 Java Nginx Linux Spring Boot

单例模式的八种写法比较

发表于 2021-08-21 | 分类于 Java | 0 | 阅读次数 712

单例模式的八种写法比较

​ 单例模式是常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。一般介绍单例模式的书籍都会提到饿汉式和懒汉式这两种实现方式。但是除了这两种方式,还有其他几种实现单例的方式。

简介

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体行为。比如在某服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象在通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

基本的实现思路

单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获取该实例的方法(必须是静态方法,通常使用getInstance 这个名称)。

单例的实现主要是通过以下两步骤:

  1. 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
  2. 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,入宫类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

注意事项

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时给自创建了一个实例,这样就有个两个实例被构造出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

单例模式的八种写法

  1. 饿汉式(静态常量)【可用】

    public class Singleton {
    
        private final static Singleton INSTANCE = new Singleton();
    
        private Singleton(){}
    
        public static Singleton getInstance(){
            return INSTANCE;
        }
    }
    
  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

  • 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

  1. 饿汉式(静态代码块)【可用】

    public class Singleton {
    
        private static Singleton instance;
    
    	static {
            instance = new Singleton();
        }
    
        private Singleton(){}
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    
  • 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化的实例。优缺点和上面的是一样的。
  1. 懒汉式(线程不安全)【不可用】

    public class Singleton {
    
        private static Singleton singleton;
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
  • 这种写法起到了Lazy Lorading, 但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null) 判断语句,还未来得及往下执行,另一个线程也通过了这个判断语句, 这时便会生产多个实例。所以在多线程环境下不可使用这种方式。
  1. 懒汉式(线程安全,同步方法)【不推荐用】

    public class Singleton {
    
        private static Singleton singleton;
    
        private Singleton() {}
    
        public static synchronized Singleton getInstance() {
            if (singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
  • 优点:解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance() 方法进行了线程同步。
  • 缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。
  1. 懒汉式(线程安全,同步代码块)【不可用】
public class Singleton {
    
    private static Singleton singleton;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}
  • 由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步生产实例化的代码块。但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入 if (singleton==null) 判断语句块,还未来得及往下执行,另一个线程也通过 了这个判断语句,这是便会生产多个实例。
  1. 双重检查【推荐用】

    public class Singleton {
    
        private static volatile Singleton singleton;
    
        private Singteon() {}
    
        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class){
                    if (singleton == null) {
                     	singleton = new Singleton();     
                    }              
                }
            }
            return singleton;
        }
    }
    
  • Double-check 概念对于多线程开发者来说不会陌生,如代码中所示,我们进行两次if(singleton==null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断 if(singleton == null),直接return 实例化对象。

  • 优点:线程安全;延迟加载;效率较高。

  1. 静态内部类【推荐用】
public class Singleton {
    
    private Singleton() {}
    
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
  • 这种方式跟饿汉式方式采用得机制类似,但又不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被SingletonInstance类,从而完成Singleton的实例化。
  • 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  • 优点:避免了线程不安全,延迟加载,效率高。
  1. 枚举【推荐用】

    public enum Singleton {
        INSTANCE;
        public void whateverMethod() {
    
        }
    }
    
  • 借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。

  • 优点:系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

  • 缺点:当想实例化 一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不见源码的时候。

  • 使用场景

    • 需要频繁进行创建和销毁的对象;
    • 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
    • 工具类对象;
    • 频繁访问数据库或文件对象;
# 设计模式
Spring Boot 最核心的 25 个注解
开发规范
  • 文章目录
  • 站点概览
Adrian

Adrian

曙光在头上,不抬起头,便永远只能看见物质的闪光。

120 日志
11 分类
70 标签
RSS
Creative Commons
Links
  • 美团技术团队
  • 阮一峰
  • 程序猿DD
  • SpringBoot 中文社区
  • 在线文档
  • Bean Searcher
  • OkHttps
  • Grails
  • Sa-Token
  • 程序员的进击之路
  • bugstack 虫洞栈
  • Java 全栈知识体系
  • Gobrs-Async
  • 查询网
  • 微信开放社区
  • 物联网技术指南
  • emqx
  • 看云
  • 深圳核酸检测点查询
  • Hutool
  • Spring
  • V2EX
  • v-charts
  • Vert.x 官方文档
  • Vert.x 官方文档中文翻译
  • 极客时间
  • Apache RocketMQ 开发者指南
  • 知了
  • 阿里云知行动手实验室
  • Learn Git Branching
  • Spring Boot 教程
  • 未读代码
  • 如梦技术
  • jpom
  • Cubic
  • Easy-Es
  • bing-wallpaper
  • solon
  • LuatOS
  • ThingsBoard
  • Linux 中国◆开源社区
  • Apache Dubbo
  • Jenkins
  • 技术文章摘抄
  • VueJS
  • MapStruct
  • elasticsearch 中文社区
  • Apollo(阿波罗)
  • TiKV文档
  • Chrome插件分享
  • 一步步搭建物联网系统(教你设计物联网系统)
  • 全栈增长工程师指南
  • 程序员的自我修养
  • Pro Git(中文版)
  • 学习 Web 开发
  • 极客教程
  • PingCAP 文档中心
  • 酷壳
  • Refactoring Guru 网站
  • 学习 Java 语言
  • smart-doc
  • mybatis-plus
  • 字母哥博客
0%
© 2023 Adrian
由 Halo 强力驱动
|
主题 - NexT.Gemini v5.1.4