前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java之单例模式

Java之单例模式

作者头像
全栈程序员站长
发布2022-06-30 13:49:15
1910
发布2022-06-30 13:49:15
举报
文章被收录于专栏:全栈程序员必看

单例模式的优点:

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

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

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

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

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

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

代码语言:javascript
复制
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底层内部模型原因,偶尔会出问题。不建议使用。

代码语言:javascript
复制
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类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
  • 兼备了并发高效调用和延迟加载的优势!
代码语言:javascript
复制
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从根本上提供保障!避免通过反射和反序列化的漏洞!

缺点:无延迟加载.

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

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

测试

代码语言:javascript
复制
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);
    }
}

结果:

代码语言:javascript
复制
com.ahzy.SingletonDome01@15db9742
com.ahzy.SingletonDome01@15db9742
true

破解单例

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

  • 反射可以破解上面几种(不包含枚举式)实现方式!(可以在构造方法中手动抛出异常控制)
  • 可以通过定义readResolve()防止获得不同对象。
  • 反序列化时,如果对象所在类定义了readResolve(),(实际是一种回调),定义返回哪个对象。
代码语言:javascript
复制
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底层内部模型原因,偶尔会出问题。不建议使用) • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载) • 枚举式(线程安全,调用效率高,不能延时加载。并且可以天然的防止反射和反序列化漏洞!)
  • 如何选用? 单例对象 占用 资源 少,不需要 延时加载:枚举式 好于 饿汉式。 单例对象 占用 资源 大,需要 延时加载: 静态内部类式 好于 懒汉式。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106710.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年7月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 饿汉式实现(单例对象立即加载)
  • 懒汉式实现(单例对象延迟加载)
  • 双重检测锁实现
  • 静态内部类实现方式(也是一种懒加载方式)
  • 枚举实现单例模式
  • 测试
  • 破解单例
  • 总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档