看这里!一文带你读懂单例模式 ╰+攻爆jí腚メ 2022-10-29 07:41 195阅读 0赞 > 微信公众号:**Java随笔录** > 关注可了解更多Java相关的技术分享。问题或建议,欢迎公众号留言! > **如果你觉得JiangNanMax对你有帮助,欢迎赞赏!** ### 文章目录 ### * * * 单例模式 * 单例模式的应用场景 * 单例模式的优缺点 * 代码实现 * * (1) 饿汉式 * (2) 懒汉式(线程不安全) * (3) 懒汉式(线程安全) * (4) 双重校验锁(double-checked locking) * (5) 静态内部类 * 公众号 * 赞赏 ### 单例模式 ### 单例模式可以说是设计模式中最简单的一个了,它属于创建型模式,其定义如下: > 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。 实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。单例模式的类图如下所示: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pfX01heA_size_16_color_FFFFFF_t_70_pic_center] ### 单例模式的应用场景 ### 对于Java来说,单例模式可以保证在一个 JVM 中只存在单一实例。单例模式的应用场景主要有以下几个方面; * 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC; * 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等; * 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用; * 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等; * 频繁访问数据库或文件的对象; * 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套; * 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如Web中的配置对象、数据库的连接池等。 ### 单例模式的优缺点 ### 单例模式的优点: * 单例模式可以保证内存里只有一个实例,减少了内存的开销; * 可以避免对资源的多重占用; * 单例模式设置全局访问点,可以优化和共享资源的访问。 单例模式的缺点: * 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则; * 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象; * 与单一职责原则冲突,一个类只应该关心其内部逻辑的实现,而不应该插手外部对其的实例化。 ### 代码实现 ### 单例模式的代码实现其实很简单,要做到一个类的实例只能由类自身进行创建,关键就是私有化其构造函数。其中,单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。结合不同的场景需求(单线程多线程)以及Java的特性(锁和类加载),就会有很多种实现方法,我这里总结了常见的五种。 #### (1) 饿汉式 #### 饿汉式实现起来简单,它基于类加载机制,不用加锁也能避免多线程同步的问题,所以执行效率会比较高。但是这种方法有个缺点,就是在类加载的时候就会进行实例化,没有达到Lazy Loading的效果。该种方法的代码实现如下: public class Singleton { private Singleton() { } private static final Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } #### (2) 懒汉式(线程不安全) #### 针对饿汉式中没有Lazy Loading的效果,可以通过如下方法来实现。不过该方法也有个最大的问题,就是只能支持在单线程环境下工作,它是线程不安全的,多线程的话会出现多个实例化对象的出现。具体代码如下: public class Singleton { private Singleton() { } private static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } #### (3) 懒汉式(线程安全) #### 针对线程不安全的问题,最简单直接的解决方法就是加锁,对getInstance( )方法加锁,这能很快的解决多线程安全问题,但是带来的问题也很明显,就是太影响性能了。具体代码如下所示: public class Singleton { private Singleton() { } private static Singleton instance; synchronized public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } #### (4) 双重校验锁(double-checked locking) #### 直接对整个getInstance( )方法上锁,能够解决线程安全问题,但会大大影响程序的性能,因为很容易出现锁竞争的情况。这里使用双重校验的机制,一方面能够解决线程安全问题,另一方面还能保持高性能。关键就是在于两个if判断,其实大部分情况下外部的if判断是不会通过的,因为只要该单例类被实例化了一次,外部if的判断条件就成立不了了,所以能够保持较好的性能;而内部的if判断,则针对的是单例类还没被实例化的时候,可能有多个线程同时通过了外部的if判断,要对单例类进行实例化,此时就需要上锁,并在临界区内再次判断实例是否已经被创建,防止多次创建,因此内部的if判断保证了只有一个实例会被创建。具体代码如下: public class Singleton { private Singleton() { } private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } #### (5) 静态内部类 #### 该方法能够达到同双重校验方式相同的效果,且实现方法更为简单,不需要借助锁就能实现。主要是借助了类加载的特性,内部静态类只有在使用的时候才会进行加载,这就能够实现Lazy Loading;且类加载时只会加载一次,天然的线程安全保证。具体代码如下: public class Singleton { private Singleton() { } private static class InnerClass { private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return InnerClass.instance; } } ### 公众号 ### * 关注公众号,即时接收关于Java的技术分享! ![20210207183855124.JPG][] ### 赞赏 ### * 如果你觉得JiangNanMax对你有帮助,欢迎赞赏,有你的支持,JiangNanMax一定会更加努力! ![20210207184039797.JPG][] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pfX01heA_size_16_color_FFFFFF_t_70_pic_center]: /images/20221024/8779285a423b4cf6b6f84a1933f55072.png [20210207183855124.JPG]: /images/20221024/c51812ac13764a6fae65d3ecfaa48db5.png [20210207184039797.JPG]: /images/20221024/1e7f72c8ef654f7fb2f2bf6171730a89.png
还没有评论,来说两句吧...