当前位置: 首页 > news >正文

单例模式总结和应用

单例模式(Singleton)

  单例模式就是创建类的一种方式,保证该类仅有一个实例,该类可以向外部提供一个其实例的全局访问点。类自身维护实例,可以保证外部随时能够访问,并且能够防止实例化多个对象。
  
实现单例类的关键:

私有构造器;
自身的私有的静态成员;
公共的静态实例访问方法。

1. 实现方案

方案一

/**
 * 单例模式:方案一
 */
public class Singleton {

    // 将构造方法私有化,防止以new的方式创建实例
    private Singleton() { }

    // 定义一个引用自身对象的属性,该属性为静态常量
    private static final Singleton instance = new Singleton();

    /**
     * 静态方法,返回该类的实例
     * @return
     */
    public static Singleton getInstance() {
        return instance;
    }
}

  该方法较为简单,而且获取的实例是静态常量,因此不存在线程安全问题,完全摒弃了synchronized造成的性能问题。然而,当该类被加载时,就会创建静态常量对象,并且该对象会一直占有内存,直到该类卸载,因此有些情况下会造成内存问题。

方案二

/**
 * 单例模式:方案二
 */
public class Singleton {

    // 将构造方法私有化,防止以new的方式创建实例
    private Singleton() { }

    // 定义一个自身类型的静态变量
    private static Singleton instance = null;

    /**
     * 静态方法,返回该类的实例
     * @return
     */
    public static Singleton getInstance() {
        // 判断该实例是否存在
        if (null == instance)
            instance = new Singleton();
        return instance;
    }
}

  方案二仅仅是基于内存的节省对方案一的改造,但是如果在多线程环境下,有可能会产生多个实例对象,因此不是线程安全的。

方案三

/**
 * 单例模式:方案三
 */
public class Singleton {

    // 将构造方法私有化,防止以new的方式创建实例
    private Singleton() {  }

    // 定义一个自身类型的静态变量
    private static Singleton instance = null;

    /**
     * 静态方法,返回该类的实例
     * 方法添加同步锁,防止多线程访问产生多个实例
     * @return
     */
    public synchronized static Singleton getInstance() {
        // 判断该实例是否存在
        if (null == instance)
            instance = new Singleton();
        return instance;
    }
}

  方案三在方案二的基础上为静态方法添加同步锁,以达到线程安全的要求。但是同步方法被频繁调用时,当然会存在效率问题。

方案四

/**
 * 单例模式:方案四
 */
public class Singleton {

    // 将构造方法私有化,防止以new的方式创建实例
    private Singleton() {  }

    // 定义一个自身类型的静态变量
    private static Singleton instance = null;

    /**
     * 静态方法,返回该类的实例
     * 方法内部添加同步块,不用每次调用方法都要获取同步锁
     * @return
     */
    public static Singleton getInstance() {
        // 判断该实例是否存在
        if (null == instance) {
            // 如果不存在,才获取同步锁,如果存在则直接返回对象
            synchronized(Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

  方案四改进了方案三,保证只有在必要的情况下,即当对象没有创建的时候才获取同步锁,如果对象已经存在,则直接返回即可,实现了高效并且线程安全。
  方案四需要注意同步块中又进行了一次判断,原因是,如果当代码执行到第一次判断时,有可能另一个线程刚好创建了一个实例,因此,获取锁之后还要判断一次。

2. 应用举例

  例如,在使用Jedis的时候,由于多线程下频繁使用,因此需要使用连接池来管理多个Jedis连接,而为了保证连接池只有一个,则需要采用单例模式,如果不用Spring来管理连接池,则需要使用单例模式:

/**
 * Redis连接池工具
 */
public class JedisPoolUtil {

    private static volatile JedisPool jedisPool = null;

    // 提供一个私有构造函数保证单例
    private JedisPoolUtil() { }

    /**
     * 获取一个<tt>JedisPool</tt>
     * @return jedisPool 一个单例的{JedisPool}
     */
    public static JedisPool getJedisPoolInstance() {

        if (null == jedisPool) {
            synchronized (JedisPool.class) {
                if (null == jedisPool) {
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    // poo2已经更改maxActive为maxTotal
                    poolConfig.setMaxTotal(32);
                    // pool2已经更改为maxWaitMillis
                    poolConfig.setMaxWaitMillis(100 * 1000);

                    jedisPool = new JedisPool(poolConfig, "0.0.0.0", 6379);
                } // if end
            } // synchronized end
        } // if end

        return jedisPool;
    }

    public static void release(JedisPool jedisPool) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
        } finally {
            if (null != jedis)  jedis.close();
        }
    }
}

相关文章:

  • ArrayList的实现
  • LinkedList实现
  • Stack的实现
  • Queue的实现
  • 更改gitlab默认端口
  • JVM运行时数据区
  • Eclipse Memory Analysis的安装和使用
  • 基于Redis实现的延迟消息队列
  • elasticsearch插件x-pack安装和使用
  • 基于Rabbitmq实现延迟消息
  • 使用拦截器进行数据加解密
  • JVM垃圾收集和内存分配
  • React Jest单元测试配置
  • acme 证书管理
  • GitLab邮箱配置
  • 2017 年终总结 —— 在路上
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • Git学习与使用心得(1)—— 初始化
  • gulp 教程
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • JS学习笔记——闭包
  • laravel 用artisan创建自己的模板
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • quasar-framework cnodejs社区
  • 电商搜索引擎的架构设计和性能优化
  • 分享几个不错的工具
  • 关于 Cirru Editor 存储格式
  • 源码安装memcached和php memcache扩展
  • python最赚钱的4个方向,你最心动的是哪个?
  • 正则表达式-基础知识Review
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • !!java web学习笔记(一到五)
  • ###C语言程序设计-----C语言学习(6)#
  • #预处理和函数的对比以及条件编译
  • (C语言)逆序输出字符串
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (区间dp) (经典例题) 石子合并
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • .htaccess 强制https 单独排除某个目录
  • .NET Micro Framework初体验
  • .net反编译的九款神器
  • .NET企业级应用架构设计系列之技术选型
  • @angular/cli项目构建--http(2)
  • @Autowired和@Resource的区别
  • @Bean注解详解
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @RequestMapping 的作用是什么?
  • @基于大模型的旅游路线推荐方案