行业资讯 2025年08月6日
0 收藏 0 点赞 546 浏览 4018 个字
摘要 :

文章目录 一、介绍 二、作用 使用场景 三、方法 1.饿汉式 2、懒汉式 3、枚举类实现(官方推荐) 四、总结 本文主要讲解关于Java单例模式代码实现的几种方法相关内……




  • 一、介绍
  • 二、作用
    • 使用场景
  • 三、方法
    • 1.饿汉式
    • 2、懒汉式
    • 3、枚举类实现(官方推荐)
  • 四、总结

本文主要讲解关于Java单例模式代码实现的几种方法相关内容,让我们来一起学习下吧!

一、介绍

采取一定的方法,让软件系统一个类只能创建和使用一个实例对象,并提供一个取得对象的方法

二、作用

单例模式保证系统中这个类只有一个对象,节省了系统资源,适当使用可以提高系统性能

使用场景

需要频繁的创建和销毁对象、创建对象耗时过多但需要经常用到的、业务要求只能有一个实例的

三、方法

1.饿汉式

  • 解析:所谓饿汉式即像恶狼一样,类一加载就迫不及待先创建出实例,私有化构造器使外部无法直接new对象
  • 优点:写法简单,类一加载便实例化,线程安全
  • 缺点:类一加载便实例化,没有懒加载的效果,如果类一直不需要实例,就会造成空间浪费
public class Pattern1 {

    private Pattern1() {

    }

    // 饿汉式1
    private static final Pattern1 instance = new Pattern1();

    public static Pattern1 getInstance() {
        return instance;
    }
}
public class Pattern1 {

    private Pattern1() {

    }

//    饿汉式2
    private static final Pattern1 instance;
    static {
        instance = new Pattern1();
    }

    public static Pattern1 getInstance() {
        return instance;
    }
}
public class Singleton {
    public static void main(String[] args) {
        Pattern1 instance1 = Pattern1.getInstance();
        Pattern1 instance2 = Pattern1.getInstance();
        System.out.println(instance1 == instance2); // true
        System.out.println(instance1); // 非空
        System.out.println(instance1.hashCode() == instance2.hashCode());// true
    }
}

2、懒汉式

(1)懒汉式1(不可用)

  • 解析:所谓懒汉式即像本身懒得创建单例,等到需要实例时再创建出实例
  • 优点:实现懒加载
  • 缺点:当多个线程同时进入instance == null地判断时可能会返回多个实例而不是单例,线程不安全
public class Pattern2 {
    private static Pattern2 instance;

    private Pattern2() {

    }

    public static Pattern2 getInstance() {
        if (instance == null) {
            instance = new Pattern2();
        }
        return instance;
    }
}

(2)懒汉式2(不推荐)

  • 目的:为了解决上述地线程安全问题
  • 实现:实现线程安全很自然想到加同步锁,添加synchronized关键字
  • 缺点:既然用到了锁,就容易出现效率问题;我们分析一下,其实只需要第一次获取实例地时候加锁就行,后续每次得到单例只需要返回单例没有线程安全问题,锁却还在,浪费时间
public class Pattern2 {
    private static Pattern2 instance;

    private Pattern2() {

    }

    public static synchronized Pattern2 getInstance() {
        if (instance == null) {
            instance = new Pattern2();
        }
        return instance;
    }
}

(3)懒汉式3(失败)

  • 目的:为了解决上述地线程安全效率低问题
  • 实现:既然只有instance == null时才会出现线程安全,不如将锁加在判断语句中,当不为null自然不会进入锁
  • 缺点:好像是解决了效率低下,然而杀敌一千自损一万,这种写法还是线程不安全的;当多个线程进入了判断语句内,instance = new Pattern2()这个语句就已经会执行多次,instance并不是单例
public class Pattern2 {
    private static Pattern2 instance;

    private Pattern2() {

    }

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

(4)懒汉式4(双重判断)

  • 目的:为了解决上述效率高但是又导致线程安全问题
  • 实现:既然将锁加在判断语句中,判断语句会有多个线程进来,不如在判断语句中的锁里面再进行一次判断,即进行双重判断
public class DoubleCheck {
    private static volatile DoubleCheck instance;

    private DoubleCheck() {

    }

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

此处instance必须加volatile注解,目的是保证变量的有序性 ,为什么要保证有序性,还得先讲讲new一个对象具体做的事

  1. 首先在常量池中去定位一个类的符号引用,并检查类是否被加载、连接、初始化过,没有就需要进行类加载
  2. 接下来为对象分配内存,有指针碰撞(连续内存使用)和空闲列表(碎片内存使用)两种分配方式
  3. 分配内存后为对象内存空间初始化为0(除对象头),那么对象不经过初始化便可使用
  4. 对对象头进行一些设置,如GC分代年龄,是否有偏向锁,hashcode(哈希码事实上在调用hashcode方法时才设置),线程ID,是否持有锁等设置
  5. 调用构造函数()方法给第三步初始化为0的字段按照用户要求设置
  6. 将引用指向新生成对象的地址

而上述这些步骤由于指令重排序的原因并不一定按顺序进行,在单线程这是没有问题的,因为单线程中执行new操作时这个线程不会同时又去使用它,一次只会执行一个操作,而多线程中如果线程A执行instance = new DoubleCheck()时先执行的new对象第6步再执行初始化对象,此时线程B判断instance == null为false(instance引用已经得到便不为null)则直接返回一个未初始化的对象并使用就出现问题了

这时给instance变量加上volatile保证new的过程不会进行重排序便不会出现问题

(5)懒汉式5(静态内部类)

  • 优点:同样是既能实现懒加载,又是线程安全的
  • 实现:利用类加载机制实现;静态内部类不会在Pattern3初始化时就加载而是会等到第一次调用getInstance方法时才会加载,这就实现了懒加载,而且JVM在加载静态内部类时是线程安全的
public class Pattern3 {

    private Pattern3() {

    }

    private static class Provider {
        private static Pattern3 instance = new Pattern3();
    }

    public static Pattern3 getInstance() {
        return Provider.instance;
    }
}

3、枚举类实现(官方推荐)

  • 优点:线程安全的,而且不会被反射破坏单例(前面的例子事实上只要你想都能够手动使用反射破坏单例)
  • 缺点:不是懒加载的
  • 实现:使用枚举类实现单例,枚举类是从JVM层面保证单例的
public enum Pattern4 {
    INSTANCE;

    public static void test() {
        System.out.println(\"Pattern4\");
    }
}

推荐写法

public class Pattern4 {

    private Pattern4() {

    }

    public static Pattern4 getInstance() {
        return Instance.INSTANCE.getInstance();
    }

    private enum Instance {
        INSTANCE;

        private Pattern4 pattern4;

        // enum类保证了只会使用一次构造函数
        Instance() {
            this.pattern4 = new Pattern4();
        }

        public Pattern4 getInstance() {
            return this.pattern4;
        }
    }
}
public class Singleton {
    public static void main(String[] args) {
        Pattern4 instance1 = Pattern4.INSTANCE;
        Pattern4 instance2 = Pattern4.INSTANCE;
        instance1.test();// 打印出Pattern4
        System.out.println(instance1.hashCode() == instance2.hashCode());// true
    }
}

四、总结

  • 单例模式保证系统中这个类只有一个对象,节省了系统资源,适当使用可以提高系统性能
  • 使用场景:需要频繁的创建和销毁对象、创建对象耗时过多但需要经常用到的、业务要求只能有一个实例的
  • 对于饿汉式,实现比较简单易懂也线程安全,如果能保证类会至少使用一次完全是可以使用的
  • 对于懒汉式,主要的就是注意线程安全问题

以上就是关于Java单例模式代码实现的几种方法相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9886.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号