Java之单例模式

Java之单例模式

单例模式的优点:

  1. 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要
    比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动
    时直接产生一个单例对象,然后永久驻留内存的方式来解决
  2. 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计
    一个单例类,负责所有数据表的映射处理
    • 常见的五种单例模式实现方式:
    – 主要:
    • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
    • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
    – 其他:
    • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)
    • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) • 枚举单例(线程安全,调用效率高,不能延时加载)

饿汉式实现(单例对象立即加载)

要点: 饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!

package com.ahzy;
/** * 单例饿汉式 * @author 晓宇码匠 * 频繁的调用这个实例的时候用饿汉式 */
public class SingletonDome01 {
   
    //类初始化时加载这个对象,没有延迟加载的优势,加载类时,是天然的线程安全
    private static SingletonDome01 instance = new SingletonDome01();
    //私有化构造器
    private SingletonDome01(){
      
    }
    //方法没有同步,调用效率高
    public static SingletonDome01 getInstance() {
   
        return instance;
    }
}

懒汉式实现(单例对象延迟加载)

要点: lazy load! 延迟加载, 懒加载! 真正用的时候才加载!
问题:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率较低。

package com.ahzy;
/** * 单例懒汉式 * @author 晓宇码匠 * 资源利用率高。但是每次调用getIntance()都要同步,并发效率低 * 当创建实例的代价较大时,用懒汉式 */
public class SingletonDome02 {
   
    private static SingletonDome02 instance;
    private SingletonDome02() {
   
    }
    //方法同步,调用效率低
    public static synchronized SingletonDome02 getInstance(){
   
        //延迟加载,懒加载,真正用的时候再加载
        if(instance==null){
   
            instance = new SingletonDome02();
        }
        return instance;
    }
}

双重检测锁实现

要点:这个模式将同步内容下方到if内部,提高了执行的效率不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了。
问题: 由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用。

package com.ahzy;
/** * 单例双重检测锁模式 * @author 晓宇码匠 * 问题:由于编译器优化原因和JVM底层模式内部原因,偶尔会出现数据调整问题,不建议使用 */
public class SingletonDome03 {
   
    private static SingletonDome03 instance = null;
    private SingletonDome03() {
   }
    /* * 这个模式将同步内容下方到if内部,提高了执行的效率 * 不必每次获得对象时都要进行同步,只有第一次才同步 * 创建了以后就没必要了。 */
    public static SingletonDome03 getInstance() {
   
        if (instance == null) {
   
            SingletonDome03 sc;
            synchronized (SingletonDome03.class) {
   
                sc = instance;
                if (sc == null) {
   
                    synchronized (SingletonDome03.class) {
   
                        if (sc == null) {
   
                            sc = new SingletonDome03();
                        }
                    }
                    instance=sc;
                }
            }
        }
        return instance;
    }
}

静态内部类实现方式(也是一种懒加载方式)

要点:

  • 外部类没有static属性,则不会像饿汉式那样立即加载对象。
  • 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
  • 兼备了并发高效调用和延迟加载的优势!
package com.ahzy;
/** * 静态类部类(也是一种懒加载) * @author 晓宇码匠 * 特点:延迟加载,调用效率高,线程安全 */
public class SingletonDome04 {
   
    private static class SingletClassInstance{
   
        private static SingletonDome04 instance = new SingletonDome04();
    }
    private SingletonDome04(){
     
    }
    public static SingletonDome04 getInstance(){
   
        return SingletClassInstance.instance;
    }
}

枚举实现单例模式

优点:

  • 实现简单.
  • 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!

缺点:无延迟加载.

package com.ahzy;
/** * 单例枚举式 * @author 晓宇码匠 * 优点:简单 * 缺点:没有延迟加载 */

public enum SingletonDome05 {
   
    //这个枚举元素,本身就是单例模式
    INSTANCE;
    //添加自己需要的操作
    public void singletonOperation() {
   
        
    }
}

测试

package com.ahzy;

public class Client {
   
    public static void main(String[] args) {
   
        SingletonDome01 s1 = SingletonDome01.getInstance(); 
        SingletonDome01 s2 = SingletonDome01.getInstance(); 
        
        System.out.println(s1);
        System.out.println(s2);
        
        System.out.println(SingletonDome05.INSTANCE==SingletonDome05.INSTANCE);
    }
}

结果:

com.ahzy.SingletonDome01@15db9742
com.ahzy.SingletonDome01@15db9742
true

破解单例

方法:反射和反序列化(不包含枚举式)
预防操作(这个一般会在开发jdk或一些jar包的时候会去用):

  • 反射可以破解上面几种(不包含枚举式)实现方式!(可以在构造方法中手动抛出异常控制)
  • 可以通过定义readResolve()防止获得不同对象。
  • 反序列化时,如果对象所在类定义了readResolve(),(实际是一种回调),定义返回哪个对象。
public class SingletonDemo01 implements Serializable {
   
    private static SingletonDemo01 s;

    private SingletonDemo01() throws Exception {
   
        if (s != null) {
   
            throw new Exception("只能创建一个对象");
            // 通过手动抛出异常,避免通过反射创建多个单例对象!
        }
    } // 私有化构造器

    public static synchronized SingletonDemo01 getInstance() throws Exception {
   
        if (s == null) {
   
            s = new SingletonDemo01();
        }
        return s;
    }

    // 反序列化时,如果对象所在类定义了readResolve(),(实际是一种回调),定义返回哪个对象。
    private Object readResolve() throws ObjectStreamException {
   
        return s;
    }
}

总结

  • 效率
模式 时间
饿汉式 22ms
静态内部类式 28ms
枚举式 32ms
双重检查锁式 65ms
懒汉式 636ms
  • 比较与特点
    主要:
    • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
    • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
    其他:
    • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)
    • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载)
    • 枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列化漏洞!)
  • 如何选用?
    单例对象 占用 资源 少,不需要 延时加载:枚举式 好于 饿汉式。
    单例对象 占用 资源 大,需要 延时加载: 静态内部类式 好于 懒汉式。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106711.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • (2020.8.20)VSCode配置java环境JDK1.8

    (2020.8.20)VSCode配置java环境JDK1.8第一步:下载1.8版本的JDK,获取JDK路径jdk1.8的下载以及配置第二步:安装VScode的java插件第三步:这是文件–打开文件夹–选择创建的文件夹(JAVA)的界面进入设置面板–选择扩展–选择JavaTestRunner–打开settings.json文件第四步:在文件末尾加上JDK的文件路径,重启VSCode参考的settings.json文件配置样式可能出现的问题:Vscode中不再支持JDK8的解决方案…

  • java ajax教程_JAVA AJAX教程第一章—初识AJAX

    java ajax教程_JAVA AJAX教程第一章—初识AJAX既然是认识AJAX,理论和实践相结合,这样让自己学的更快,理解更深入,我分一下几点:1、认识传统的同步交互方式和AJAX解决方案2、AJAX使用到的技术3、实例体验AJAX一、同步交互方式和AJAX解决方案传统的WEB应用是同步交互的方式,这种同步交互方式的处理过程如下图什么是同步交互方式:首先,用户向HTTP服务器提交一个处理请求。接着,服务器端接收到请求后,按照预先编写好的程序中的…

  • Redisson 实现分布式锁

    Redisson 实现分布式锁

  • C语言 strstr函数的用法及模拟实现strstr函数「建议收藏」

    C语言 strstr函数的用法及模拟实现strstr函数「建议收藏」C语言strstr函数的用法及模拟实现strstr函数一、strstr函数的用法二、模拟实现strstr函数的功能一、strstr函数的用法1.strstr函数原型:char*strstr(constchar*str1,constchar*str2)2.功能:strstr()是一个参数为两个字符指针类型,返回值是char*类型的函数,它用于找到子串(str2)在一个字符串(str1)中第一次出现的位置。这里因为传进来的地址指向的内容不会在发生改变,所以我们在两个形参(char*)前加上c

  • 相关性分析的五种方法有哪些_数据相关性分析

    相关性分析的五种方法有哪些_数据相关性分析相关分析(AnalysisofCorrelation)是网站分析中经常使用的分析方法之一。通过对不同特征或数据间的关系进行分析,发现业务运营中的关键影响及驱动因素。并对业务的发展进行预测。本篇文章将介绍5种常用的分析方法。在开始介绍相关分析之前,需要特别说明的是相关关系不等于因果关系。相关分析的方法很多,初级的方法可以快速发现数据之间的关系,如正相关,负相关或不相关。中级的方法可以对数据间关系的强弱进行度量,如完全相关,不完全相关等。高级的方法可以将数据间的关系转化为模型,并通过模型对未来的业.

  • JS如何取整数

    JS如何取整数1.丢弃小数部分,保留整数部分parseInt(5/2)2.向上取整,有小数就整数部分加1Math.ceil(5/2)3,四舍五入.Math.round(5/2)4,向下取整Math.floor(5/2)

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号