热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

项目实战—那些年常用的单例模式

常见的单例模式:饿汉式、懒汉式、双重检查锁模式、静态内部类实现单例模式、枚举单例模式,本文重点是在项目中如何实现上述的单例模式。

常见的单例模式:饿汉式、懒汉式、双重检查锁模式、静态内部类实现单例模式、枚举单例模式,本文重点是在项目中如何实现上述的单例模式。

1. 饿汉式单例模式

饿汉式单例:类初始化时将单例对象加载到JVM中。

/**
 * 饿汉单例模式。
 * 类初始化时将单例对象加载到JVM中。
 */
@Slf4j
public class Singletoneh {
    private final static Singletoneh instance = new Singletoneh();

    private Singletoneh() {
    }

    public static Singletoneh getInstance() {
        return instance;
    }

    public void say() {
        System.out.println("【饿汉模式】—实现单例!");
    }

}
2. 懒汉式单例模式

懒汉式单例:并发写时,存在线程安全问题。进化版:双重检查锁模式

/**
 * 懒汉式单例
 */
public class Singletonlh {
    private static Singletonlh instance;
    private Singletonlh() {

    }
    public static Singletonlh getInstance() {
        if (instance == null) {
            instance = new Singletonlh();
        }
        return instance;
    }
    public void say() {
        System.out.println("【懒汉模式】—实现单例!");
    }
}
3. 双重检查锁模式

volatile关键字详见—Volatile可见性原理

/**
 * 双重检查锁模式
 * volatile 关键字:防止指令重排
 * 

* 被volatile修饰的变量,会加一个lock前缀的汇编指令。 * 若变量被修改后,会立刻将变量由工作内存回写到主存中。那么意味了之前的操作已经执行完毕。这就是内存屏障。 */ public class SingletonOfSync2 { private static volatile SingletonOfSync2 instance; private SingletonOfSync2() { } /** * 双重检查模式,防止并发写时创建多个实例对象。 * 使用volatile关键字防止指令重排; * * @return */ public static SingletonOfSync2 getInstance() { if (instance == null) { synchronized (SingletonOfSync2.class) { if (instance == null) { instance = new SingletonOfSync2(); } } } return instance; } public void say() { System.out.println("【双重检查锁模式】—实现单例!"); } }

4. 静态内部类单例模式

由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由static修饰,保证只被实例化一次,并且严格保证实例化顺序。

/**
 * 静态内部类实现单例模式
 * 

* 由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。 * 静态属性由static修饰,保证只被实例化一次,并且严格保证实例化顺序。 */ public class SingletonOfInner { private SingletonOfInner() { } private static class InstanceHolder { private final static SingletonOfInner instance = new SingletonOfInner(); } public static SingletonOfInner getInstance() { return InstanceHolder.instance; } public void say() { System.out.println("【静态内部类模式】—实现单例!"); } }

5. 枚举类单例模式

因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式。

public class SingletonOfEnum {

    //私有构造方法
    private SingletonOfEnum() {

    }
    /**
     * 枚举类返回单例对象
     */
    public static SingletonOfEnum getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {

        INSTANCE;

        private final SingletonOfEnum instance;

        Singleton() {
            instance = new SingletonOfEnum();
        }

        private SingletonOfEnum getInstance() {
            return instance;
        }

    }

    public void say() {
        System.out.println("【枚举模式】—实现单例!");
    }

}
6. 破坏单例模式以及解决方案

6.1 反射破坏

public class testSingleton {
    public static void main(String[] args) throws Exception {

        Constructor co1 = Singletoneh.class.getDeclaredConstructor();
        co1.setAccessible(true);
        Singletoneh s1 = co1.newInstance();
        s1.say();

        Constructor co2 = Singletoneh.class.getDeclaredConstructor();
        co2.setAccessible(true);
        Singletoneh s2 = co2.newInstance();
        s2.say();

        System.out.println("单例对象是否相等:" + (s1 == s2));
        
    }
}
项目实战—那些年常用的单例模式
image.png

除枚举方式外, 其他方法都会通过反射的方式破坏单例,反射是通过调用构造方法生成新的对象,所以如果我们想要阻止单例破坏,可以在构造方法中进行判断,若已有实例, 则阻止生成新的实例,解决办法如下:

    private Singletoneh() {
        if(instance!=null){
            throw new RuntimeException("单例对象已经存在");
        }
    }

6.2 序列化接口Serializable

如果单例类实现了序列化接口Serializable, 就可以通过反序列化破坏单例,所以我们可以不实现序列化接口,如果非得实现序列化接口,可以重写反序列化方法readResolve(), 反序列化时直接返回相关单例对象。

public Object readResolve() throws ObjectStreamException {
    return instance;
}
推荐阅读

枚举类来实现单例模式


推荐阅读
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • PHP反射API的功能和用途详解
    本文详细介绍了PHP反射API的功能和用途,包括动态获取信息和调用对象方法的功能,以及自动加载插件、生成文档、扩充PHP语言等用途。通过反射API,可以获取类的元数据,创建类的实例,调用方法,传递参数,动态调用类的静态方法等。PHP反射API是一种内建的OOP技术扩展,通过使用Reflection、ReflectionClass和ReflectionMethod等类,可以帮助我们分析其他类、接口、方法、属性和扩展。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文介绍了Java中Currency类的getInstance()方法,该方法用于检索给定货币代码的该货币的实例。文章详细解释了方法的语法、参数、返回值和异常,并提供了一个示例程序来说明该方法的工作原理。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
author-avatar
羽书uv吧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有