Java单例模式的五种实现方式

 更新时间:2022年06月17日 10:33:46   作者:小码code  
单例模式(Singleton Pattern)是Java中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,下面这篇文章主要给大家介绍了关于Java单例模式的五种实现方式 ,需要的朋友可以参考下

前言

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

饿汉单例

是否多线程安全:是

是否懒加载:否

正如名字含义,饿汉需要直接创建实例。

public class EhSingleton {
    private static EhSingleton ehSingleton = new EhSingleton();
    private EhSingleton() {}
    public static EhSingleton getInstance(){
        return ehSingleton;
    }
}

缺点: 类加载就初始化,浪费内存

优点: 没有加锁,执行效率高。还是线程安全的实例。

懒汉单例

懒汉单例,在类初始化不会创建实例,只有被调用时才会创建实例。

非线程安全的懒汉单例

是否多线程安全:否

是否懒加载: 是

public class LazySingleton {
    private static LazySingleton ehSingleton;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (ehSingleton == null) {
            ehSingleton = new LazySingleton();
        }
        return ehSingleton;
    }
}

实例在调用 getInstance 才会创建实例,这样的优点是不占内存,在单线程模式下,是安全的。但是多线程模式下,多个线程同时执行 if (ehSingleton == null) 结果都为 true,会创建多个实例,所以上面的懒汉单例是一个线程不安全的实例。

加同步锁的懒汉单例

是否多线程安全:是

是否懒加载: 是

为了解决多个线程同时执行 if (ehSingleton == null) 的问题,getInstance 方法添加同步锁,这样就保证了一个线程进入了 getInstance 方法,别的线程就无法进入该方法,只有执行完毕之后,其他线程才能进入该方法,同一时间只有一个线程才能进入该方法。

public class LazySingletonSync {
    private static LazySingletonSync lazySingletonSync;
    private LazySingletonSync() {}
    public static synchronized LazySingletonSync getInstance() {
        if (lazySingletonSync == null) {
            lazySingletonSync =new LazySingletonSync();
        }
        return lazySingletonSync;
    }
}

这样配置虽然保证了线程的安全性,但是效率低,只有在第一次调用初始化之后,才需要同步,初始化之后都不需要进行同步。锁的粒度太大,影响了程序的执行效率。

双重检验懒汉单例

是否多线程安全:是

是否懒加载:是

使用 synchronized 声明的方法,在多个线程访问,比如A线程访问时,其他线程必须等待A线程执行完毕之后才能访问,大大的降低的程序的运行效率。这个时候使用 synchronized 代码块优化执行时间,减少锁的粒度

双重检验首先判断实例是否为空,然后使用 synchronized (LazySingletonDoubleCheck.class) 使用类锁,锁住整个类,执行完代码块的代码之后,新建了实例,其他代码都不走 if (lazySingletonDoubleCheck == null) 里面,只会在最开始的时候效率变慢。而 synchronized 里面还需要判断是因为可能同时有多个线程都执行到 synchronized (LazySingletonDoubleCheck.class) ,如果有一个线程线程新建实例,其他线程就能获取到 lazySingletonDoubleCheck 不为空,就不会再创建实例了。

public class LazySingletonDoubleCheck {
    private static LazySingletonDoubleCheck lazySingletonDoubleCheck;
    private LazySingletonDoubleCheck() {}
    public static LazySingletonDoubleCheck getInstance() {
        if (lazySingletonDoubleCheck == null) {
            synchronized (LazySingletonDoubleCheck.class) {
                if (lazySingletonDoubleCheck == null) {
                    lazySingletonDoubleCheck = new LazySingletonDoubleCheck();
                }
            }
        }
        return lazySingletonDoubleCheck;
    }
}

静态内部类

是否多线程安全:是

是否懒加载:是

外部类加载时,并不会加载内部类,也就不会执行 new SingletonHolder(),这属于懒加载。只有第一次调用 getInstance() 方法时才会加载 SingletonHolder 类。而静态内部类是线程安全的。

静态内部类为什么是线程安全

静态内部类利用了类加载机制的初始化阶段 方法,静态内部类的静态变量赋值操作,实际就是一个 方法,当执行 getInstance() 方法时,虚拟机才会加载 SingletonHolder 静态内部类,

然后在加载静态内部类,该内部类有静态变量,JVM会改内部生成方法,然后在初始化执行方法 —— 即执行静态变量的赋值动作。

虚拟机会保证 方法在多线程环境下使用加锁同步,只会执行一次 方法。

这种方式不仅实现延迟加载,也保障线程安全。

public class StaticClass {
    private StaticClass() {}
    private static class SingletonHolder {
        private static final SingletonHolder INSTANCE = new SingletonHolder();
    }

    public static final SingletonHolder getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

总结

  • 饿汉单例类加载就初始化,在没有加锁的情况下实现了线程安全,执行效率高。但是无论有没有调用实例都会被创建,比较浪费内存。
  • 为了解决内存的浪费,使用了懒汉单例,但是懒汉单例在多线程下会引发线程不安全的问题。
  • 不安全的懒汉单例,使用 synchronized 声明同步方法,获取实例就是安全了。
  • synchronized 声明方法每次线程调用方法,其它线程只能等待,降低了程序的运行效率。
  • 为了减少锁的粒度,使用 synchronized 代码块,因为只有少量的线程获取实例,实例是null,创建实例之后,后续的线程都能获取到线程,也就无需使用锁了。可能多个线程执行到 synchronized ,所以同步代码块还需要再次判断一次。
  • 静态内部类赋值实际是调用 方法,而虚拟机保证 方法使用锁,保证线程安全。

到此这篇关于Java单例模式的五种实现方式的文章就介绍到这了,更多相关Java单例模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot 中密码加密的两种方法

    Spring Boot 中密码加密的两种方法

    这篇文章主要介绍了Spring Boot 中密码加密的两种方法,帮助大家更好的理解和使用Spring Boot框架,感兴趣的朋友可以了解下
    2020-09-09
  • Java 将list集合数据按照时间字段排序的方法

    Java 将list集合数据按照时间字段排序的方法

    这篇文章主要介绍了Java 将list集合数据按照时间字段排序,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • SpringBoot实现文件在线预览功能的全过程

    SpringBoot实现文件在线预览功能的全过程

    我们开发业务系统的时候,经常有那种文档文件在线预览的需求,下面这篇文章主要给大家介绍了关于SpringBoot实现文件在线预览功能的相关资料,需要的朋友可以参考下
    2021-11-11
  • Java中的设计模式与7大原则归纳整理

    Java中的设计模式与7大原则归纳整理

    本篇文章主要对Java中的设计模式如,创建型模式、结构型模式和行为型模式以及7大原则进行了归纳整理,需要的朋友可以参考下
    2017-04-04
  • 一篇文章带你了解Maven的坐标概念以及依赖管理

    一篇文章带你了解Maven的坐标概念以及依赖管理

    这篇文章主要为大家介绍了Maven的坐标概念以及依赖管理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • 详解在springmvc中解决FastJson循环引用的问题

    详解在springmvc中解决FastJson循环引用的问题

    本篇文章主要介绍了在springmvc中解决FastJson循环引用的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Intellij IDEA 旗舰版创建 Spring MVC 项目踩过的坑

    Intellij IDEA 旗舰版创建 Spring MVC 项目踩过的坑

    IDEA旗舰版可以直接创建Spring MVC项目,但创建后的项目并不是直接就可以运行,还需要进行一些配置。这篇文章主要介绍了Intellij IDEA 旗舰版创建 Spring MVC 项目踩坑记 ,需要的朋友可以参考下
    2020-03-03
  • IDEA一键生成方法的序列图神操作

    IDEA一键生成方法的序列图神操作

    为了能够更快更清晰地搞清对象之间的调用关系,我经常需要用到序列图。手动画序列图还是很麻烦费时的,不过 IDEA 提供了一个叫做SequenceDiagram 的插件帮助我们解决这个问题。通SequenceDiagram 这个插件,我们一键可以生成时序图
    2022-01-01
  • 详解springcloud之服务注册与发现

    详解springcloud之服务注册与发现

    本次分享的是关于springcloud服务注册与发现的内容,将通过分别搭建服务中心,服务注册,服务发现来说明,非常具有实用价值,需要的朋友可以参考下
    2018-06-06
  • 详解hibernate双向多对多关联映射XML与注解版

    详解hibernate双向多对多关联映射XML与注解版

    本篇文章主要介绍了详解hibernate双向多对多关联映射XML与注解版,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05

最新评论