Java 所有 单例模式 实现及优缺点总结

单例模式作为开发过程中最常用的设计模式之一,是程序员必须了解和掌握的一种设计模式,虽然单例的实现方式和手段有很多种,但每一种都有着各自的优缺点,你是否真正的了解了各自的差异?最终如何在代码中运用,就需要我们对每一种实现方式都了如指掌方可运筹帷幄。

以下介绍了Java 实现单例的全部方式、优缺点以及那些方式是推荐使用,那些是不建议使用,一起来看一下。

单例的实现方式

静态常量饿汉式

  • 代码

    vbnet 复制代码
    public class Single {
        private static Single single = new Single();
        
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        public static Single getInstance() {
            return single;
        }
    }
  • 优点

    • 线程安全;
    • 实现起来容易,代码容易理解;
    • 没有锁存在,执行性能高;
  • 缺点

    • 没有实现懒加载,可能产生垃圾对象(即使不用,也会实例化),如果能确定这个对象必定会使用,可以考虑这种方式
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

静态代码块饿汉式

  • 代码

    vbnet 复制代码
    public class Single {
        private static Single single = null;
        
        static{
          single = new Single();
        }
        
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        public static Single getInstance() {
            return single;
        }
    }
  • 优点

    • 线程安全;
    • 实现起来容易,代码容易理解;
    • 没有锁存在,执行性能高
  • 缺点

    • 没有实现懒加载,可能产生垃圾对象,如果能确定这个对象必定会使用,可以考虑这种方式
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

懒汉式一(不要使用)

  • 代码

    csharp 复制代码
    public class Single {
    
        private static Single single = null;
        
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        private static Single getInstance() {
            // 当多个线程同时执行带这里的时候,会创建出多个对象
            if (null == single) {
                single = new Single();
            }
            return single;
        }
    }
  • 优点

    • 实现了懒加载;
    • 实现起来容易,代码容易理解;
  • 缺点

    • 非线程安全,无法保证单例,所以,不要使用。
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

懒汉式二(不推荐使用)

  • 代码

    vbnet 复制代码
    public class Single {
    
        private static Single single = null;
    
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        private static synchronized Single getInstance() {
            if (null == single) {
                single = new Single();
            }
    
            return single;
        }
    }
  • 优点

    • 实现了懒加载;
    • 线程安全
    • 实现起来容易,代码容易理解;
  • 缺点

    • 锁粒度太粗,导致执行的性能会下降
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

懒汉式三(不要使用)

  • 代码

    csharp 复制代码
    public class Single {
    
        private static Single single = null;
    
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        private static Single getInstance() {
            if (null == single) {
                // 当多个线程通知执行到这里来了之后,无法保证对象单一
                synchronized(Single.class){
                    single = new Single();
                }
            }
    
            return single;
        }
    }
  • 优点

    • 实现了懒加载;
    • 实现难度一般,代码容易理解;
  • 缺点

    • 线程不安全,无法保证对象单一,所以,不要使用。
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

双重检查DCL(推荐使用)

  • 代码

    csharp 复制代码
    public class Single {
        //这里一定要加volatile 否则可能因为指令重排的问题导致对象未初始化完成的情况
        private volatile static Single single = null;
    
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        public static Single getInstance() {
            if (null == single) {
                synchronized (Single.class) {
                    if (null == single) {
                        single4 = new Single();
                    }
                }
            }
            return single;
        }
    }
  • 优点

    • 实现了懒加载;
    • 线程安全
    • 锁粒度较细,只有第一次初始化的时候会走到synchronized部分
  • 缺点

    • 实现起来相对复杂,对于volatile的理解会比较的难
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

静态内部类(推荐使用)

  • 代码(内部类的方式)

    vbnet 复制代码
    public class Single {
    
        // 一定要将默认构造方法设置为private 否则反射实例化将破坏单例
        private Single() {
        }
    
        private static class LazyHolder {
            private static final Single INSTANCE = new Single();
        }
    
        public static Single getInstance() {
            return LazyHolder.INSTANCE;
        }
    }
  • 优点

    • 实现了懒加载;
    • 线程安全
    • 没有锁,性能较好
  • 缺点

    • 实现及理解起来相对复杂
    • 存在构造方式未设置private而导致反射实例化破坏单例的风险

枚举单例(推荐使用)

  • 代码

    csharp 复制代码
    public enum Single6
    {
        INSTANCE;
    
        public void test()
        {
            System.out.println("test()");
        }
    }
  • 优点

    • 线程安全;
    • 没有锁,性能较好;
    • 实现简单,理解容易;
    • 不会因构造方法未设置为private而带来的破坏单例的风险.
  • 缺点

    • 未实现了懒加载;

总结

以上列举了开发过程中所有的单例实现方式,同时还列出了详细的其详细的优缺点;对于枚举方式、DCL方式以及静态内部类的方式是个人相对比较推荐的方式;可以视情况使用;至于饿汉式的两种写法,由于比较简单,如果对应的单例对象在系统中必定用到而且频繁使用,也可以考虑使用;所以,没有更好,只有最好!合适才好!!!

相关推荐
一枚小小程序员哈9 分钟前
基于微信小程序的家教服务平台的设计与实现/基于asp.net/c#的家教服务平台/基于asp.net/c#的家教管理系统
后端·c#·asp.net
楽码38 分钟前
自动修复GoVet:语言实现对比
后端·算法·编程语言
石榴树下40 分钟前
00. 马里奥的 OAuth 2 和 OIDC 历险记
后端
uhakadotcom41 分钟前
开源:subdomainpy快速高效的 Python 子域名检测工具
前端·后端·面试
伍哥的传说1 小时前
Vue 3.5重磅更新:响应式Props解构,让组件开发更简洁高效
前端·javascript·vue.js·defineprops·vue 3.5·响应式props解构·vue.js新特性
似水流年流不尽思念1 小时前
容器化技术了解吗?主要解决什么问题?原理是什么?
后端
Java水解1 小时前
Java中的四种引用类型详解:强引用、软引用、弱引用和虚引用
java·后端
i听风逝夜1 小时前
看好了,第二遍,SpringBoot单体应用真正的零停机无缝更新代码
后端
德育处主任1 小时前
p5.js 3D 形状 "预制工厂"——buildGeometry ()
前端·javascript·canvas
Mintopia1 小时前
React 牵手 Ollama:本地 AI 服务对接实战指南
前端·javascript·aigc