单例模式的要点(写出一个单例模式)

目录一、单例模式的定义和应用场景(一)定义及基本要点(二)应用场景二、饿汉式单例模式(一)基本代码展示分析(二)基本分析和建议三、懒汉式单例模式(双重检查锁)(一)基本代码展示分析(二)基本分析和建议四、静态内部类实现单例模式(一)基本代码展示分析(二)基本分析和建议五、注册式单例模式(一)枚举式单例模式代码及分析:(EffectiveJa…

大家好,又见面了,我是你们的朋友全栈君。

目录

一、单例模式的定义和应用场景

(一)定义及基本要点

(二)应用场景

二、饿汉式单例模式

(一)基本代码展示分析

(二)基本分析和建议

三、懒汉式单例模式(双重检查锁)

(一)基本代码展示分析

(二)基本分析和建议

四、静态内部类实现单例模式

(一)基本代码展示分析

(二)基本分析和建议

五、注册式单例模式

(一)枚举式单例模式代码及分析:(Effective Java推荐单例模式)

(二)容器式单例模式代码及分析:(适用于实例非常多的情况,便于管理,但是是非线程安全的)

参考书籍、文献和资料


一、单例模式的定义和应用场景

(一)定义及基本要点

单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

该模式有三个基本要点

  • 一是这个类只能有一个实例;
  • 二是它必须自行创建这个实例;
  • 三是它必须自行向整个系统提供这个实例。

(二)应用场景

应用场景:J2EE中的ServlertContext、SerletContextConfig等、Spring框架应用中的ApplicationContext、数据库连接池等。

二、饿汉式单例模式

(一)基本代码展示分析

/**
 * 描述:饿汉式单例模式
 * 优点:没有任何锁,执行效率高,用户体验比懒汉式单例模式更好
 * 缺点:类加载的时候就初始化,不管用不用都占内存空间
 * 建议:适用于单例模式较少的场景
 *      如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
 *      如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。
 *
 * @author yanfengzhang
 * @date 2020-01-02 20:39
 */
public class HungrySingleton {
    private final static HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return HUNGRY_SINGLETON;
    }
}

(二)基本分析和建议

优点:没有任何锁,执行效率高,用户体验比懒汉式单例模式更好
缺点:类加载的时候就初始化,不管用不用都占内存空间
建议:

  • 适用于单例模式较少的场景
  • 如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
  • 如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。

三、懒汉式单例模式(双重检查锁)

(一)基本代码展示分析

/**
 * 描述:懒汉式单例模式---双重检查锁
 * 相比单锁而言,双重检查锁性能上虽然有提升,但是依旧用到了synchronized关键字总归要上锁,对程序性能还是存在一定的性能影响
 * 不算最优--存在优化空间
 *
 * 建议:如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
 *      如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。
 *
 * @author yanfengzhang
 * @date 2020-01-02 20:53
 */
public class LazyDoubleCheckSingleton {
    /**
     * volatile 关键字可以保证线程间变量的可见性,还有一个作用就是阻止局部重排序的发生
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;

    private LazyDoubleCheckSingleton() {
    }

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

(二)基本分析和建议

相比单锁而言,双重检查锁性能上虽然有提升,但是依旧用到了synchronized关键字总归要上锁,对程序性能还是存在一定的性能影响。注意里面volatile的使用!!!

建议:

  • 如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
  • 如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。

四、静态内部类实现单例模式

(一)基本代码展示分析

/**
 * 描述:屏蔽饿汉式单例模式的内存浪费问题和双重检查锁中synchronized的性能问题
 * 避免因为反射破坏单例
 *
 * @author yanfengzhang
 * @date 2020-01-02 21:08
 */
public class LazyInnerClassSingleton {
    /**
     * 使用LazyInnerClassSingleton的时候会先默认初始化换内部类
     * 如果没有使用,则内部类是不加载的
     */
    private LazyInnerClassSingleton() {
        /*为了避免反射破坏单例,需要在构造方法中增加限制,一旦出现多次重复创建,直接抛出异常*/
        if (null != Lazyholder.LAZY_INNER_CLASS_SINGLETON) {
            throw new RuntimeException("创建LazyInnerClassSingleton异常,不允许创建多个实例!");
        }
    }

    /**
     * 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、重载
     */
    public static final LazyInnerClassSingleton getInstance() {
        /*在返回结果前,一定会先加载内部类*/
        return Lazyholder.LAZY_INNER_CLASS_SINGLETON;
    }

    /**
     * 默认不加载
     */
    private static class Lazyholder {
        private static final LazyInnerClassSingleton LAZY_INNER_CLASS_SINGLETON = new LazyInnerClassSingleton();
    }
}

(二)基本分析和建议

屏蔽饿汉式单例模式的内存浪费问题和双重检查锁中synchronized的性能问题,同时考虑避免因为反射破坏单例问题。

相对而言性能最好!

五、注册式单例模式

注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。

注册单例模式有两种:枚举式单例模式+容器式单例模式

(一)枚举式单例模式代码及分析:(Effective Java推荐单例模式)

/**
 * 描述:注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。
 * 注册单例模式有两种:枚举式单例模式+容器式单例模式
 * 此为枚举式单例模式---Effective Java推荐单例模式
 *
 * @author yanfengzhang
 * @date 2020-01-03 09:59
 */
public enum EnumSingleton {
    /*枚举式单例模式*/
    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

(二)容器式单例模式代码及分析:(适用于实例非常多的情况,便于管理,但是是非线程安全的)

/**
 * 描述:注册式单例模式/登记式单例模式,将每个实例都登记到一个地方,使用唯一的标识获取单例。
 * 注册单例模式有两种:枚举式单例模式+容器式单例模式
 * 建议:容器式单例模式适用于实例非常多的情况,便于管理,但是是非线程安全的。
 *
 * @author yanfengzhang
 * @date 2020-01-03 10:51
 */
public class ContainerSingleton {
    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (ioc.containsKey(className)) {
                return ioc.get(className);
            }
            Object obj = null;
            try {
                obj = Class.forName(className).newInstance();
                ioc.put(className, obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return obj;
        }
    }
}

 

参考书籍、文献和资料

1.《Sring 5 核心原理与30个类手写实战》,谭勇徳,中国公信出版社,2019.

2.极客时间课程《Java性能调优实战》,刘超,2019.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/125954.html原文链接:https://javaforall.net

(0)
上一篇 2022年4月18日 上午10:20
下一篇 2022年4月18日 上午10:20


相关推荐

  • Cursor+Claude辅助建站保姆级教程:AI驱动的高效开发实践

    Cursor+Claude辅助建站保姆级教程:AI驱动的高效开发实践

    2026年3月15日
    3
  • Generic Host Process for Win32 Services 解决之道[通俗易懂]

    Generic Host Process for Win32 Services 解决之道[通俗易懂]开机有时候会出现:GenericHostProcessforWin32Services遇到了一个问题,需要关闭。出现上面这个错误一般有三种情况。1.就是病毒。开机后会提示GenericHostProcessforWin32Services遇到问题需要关闭”“RemoteRrocedureCall(RPC)服务意外终止,然后就自动重起电脑。一般该病毒会在

    2022年10月12日
    4
  • gamma校正什么意思_串联滞后校正对系统性能的影响

    gamma校正什么意思_串联滞后校正对系统性能的影响【Gamma的由来】首先,要区分照度和亮度,照度是一个客观的量,亮度是一个主观的量,不同的人看相同照度的物体所感受到的亮度是不一样的。对于照度线性变化的物体,人眼感受到的亮度不是线性的。人眼对于低照度的物体更敏感,这意味着对于照度为2、3、4的三个物体,人眼能够区分,而对于照度为222、223、224的三个物体,人眼不能区分。其次,我们存储颜色的空间是有限的,常用的RGBA32格式,每个颜色通道只有8位,最多能表示256种照度,而现实世界中的照度远超256。基于人眼对照度的感知特点,我们不能线性的去

    2026年2月15日
    4
  • cmd炫酷代码大全「建议收藏」

    cmd炫酷代码大全「建议收藏」事先准备:新建一个txt,后缀名改成cmd(或bat)里面写代码即可声明:文章目录cmd炫酷代码大全1.循环类1.1.黑客王国1.2.命令tree1.3.我是这条街,这条街,最靓的仔1.4.数字点阵1.5.数字王国加强版2.其他类2.1.观看星球大战完结cmd炫酷代码大全1.循环类1.1.黑客王国coloraechooff:123echo0101010010100101010101010101010101010101010101010101001010101echo10101

    2022年6月25日
    239
  • MODBUS规约详解

    MODBUS规约详解MODBUS 是一种主从规约 它允许读或写一个或多个字 16 位 操作 但任何情况下都不支持字节的读写 信息交换是以主站采取主动实现的 即由主机启动交换 除广播命令外 所有的一个完整交换由下行和上行两个报文组成 下行报文 主站发出的一个请求 上行报文 从站发回的一个回答 1 通信参数和传输特性传输方式 异步串行通信方式 MODBUS 规约模式 RTU 模式传输速率 2400bps 9600bp

    2026年3月18日
    2
  • java游戏开发入门教程_JAVA制作游戏入门教程

    java游戏开发入门教程_JAVA制作游戏入门教程现在流行的游戏似乎都是用C或C++来开发的。在java平台上几乎没有很大型及可玩的流行游戏。由于java是个新生语言,他的许多特性还有待大家的发掘,但是我们不能否认Java在游戏编程方面的强大性。本文将带领大家一步一步学习编写Java游戏。最终打造属于自己的Java游戏。在开始之前我们得确认你已经安装了JavaJDK,并已经安装了浏览器软件如IE。本章是以Internet为开发对象,一步一步教大…

    2022年7月7日
    24

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号