zoukankan      html  css  js  c++  java
  • 工作中的点点滴滴单例的使用

    背景:

      最近上线的交易系统已经大面积推广使用了,然后首页商品信息为了加载速度快一些,我把商品都缓存在了redisl里面,这样就避免了总是去查询数据库,本以为这样就可以了,但是在下午高峰期的时候,还是会出现加载缓慢的情况。然后通过log查询,在调用redis的时候有将近200ms的耗时。经过进一步的分析,发现在每一个调用redis.getKey的地方都会去实例化一个redis操作对象,这个过程会伴随了连接创建。所以没想到会在这里“翻船”了。于是想着把redis客户端做成单例的,这样的话每次在调用redis的时候就不会频繁的再去实例化了。

    单例模式特点:

      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。使用Singleton的好处还在于一个可以节省内存,第二个避免频繁的创建和销毁大对象,适当的提高效率。所以可以看出来它具有这些特点:1,单例类只能有一个实例;2,单例类必须自己创建自己的唯一实例;3,单例类必须给所有其他对象提供这一实例。在很多操作中,比如建立目录、数据库连接都需要这样的单线程操作。还有, singleton能够被状态化; 这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。在计算机系统中,操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。l另外我们熟悉的Windows的Task Manager(任务管理器)就是很典型的单例模式,你能打开两个windows task manager吗? ⁣ ⁣ ⁣ ⁣ Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程 内存等概念有相当的了解。

    单利模式的几种常用实现:

      通用的单例模式创建思想一般分为三步,第一步,首先使用private修改该类构造器,从而将其隐藏起来,避免程序自由创建该类实例;第二步,然后提供一个public方法获取该类实例,且此方法必须使用static修饰(调用之前还不存在对象,因此只能用类调用;第三部,最后该类必须缓存已经创建的对象,否则该类无法知道是否曾经创建过实例,也就无法保证只创建一个实例。为此,该类需要一个静态属性来保持曾经创建的实例。 ⁣ ⁣ ⁣ Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类线程内存等概念有相当的了解。

      第一种:饿汉模式,他是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,于是在装载类的时候就创建对象实例。饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

     1 /**
     2  * @author yk
     3  * @version 1.0
     4  * @describe 饿汉单例模式
     5  * @date 09-29 18:13
     6  */
     7 public class EagerSingleton {
     8     private static final EagerSingleton eagerSingleton = new EagerSingleton();
     9     private EagerSingleton() {
    10 
    11     }
    12     public static EagerSingleton getInstance() {
    13         return eagerSingleton;
    14     }
    15 }
    16 
    17 /**
    18  * @author yk
    19  * @version 1.0
    20  * @describe 静态块饿汉单例模式
    21  * @date 09-29 18:13
    22  */
    23 public class StaticEagerSingleton {
    24 
    25     private static final StaticEagerSingleton staticEagerSingleton;
    26 
    27     static {
    28         staticEagerSingleton = new StaticEagerSingleton();
    29     }
    30 
    31     private StaticEagerSingleton() {
    32 
    33     }
    34 
    35     public static StaticEagerSingleton getInstance() {
    36         return staticEagerSingleton;
    37     }
    38 }

      第二种:懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例。懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间,由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。

     1 /**
     2  * @author yk
     3  * @version 1.0
     4  * @describe 懒汉式,需要的时候才创建
     5  * @date 09-29 18:19
     6  */
     7 public class LazySingleton {
     8     //静态属性用来缓存创建实例
     9     private static LazySingleton instance = null;
    10     //私有构造方法避免程序自由创建实例
    11     private LazySingleton() {
    12     }
    13     //静态公共方法用于取得该类实例
    14     public static synchronized LazySingleton getLazySingletonInstance() {
    15         if (instance == null) {
    16             instance = new LazySingleton();
    17         }
    18         return instance;
    19     }
    20 }

      第三种,双重检查,可以使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受很大的影响。它并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。它的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,如果被其他线程修改后,当前线程是可见的,从而确保多个线程能正确的处理该变量。

     1 /**
     2  * @author yk
     3  * @version 1.0
     4  * @describe panda-example
     5  * @date 09-29 18:53
     6  */
     7 public class DoubleCheckedSingleton {
     8     //被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
     9     private volatile static DoubleCheckedSingleton instance = null;
    10     //私有构造方法
    11     private DoubleCheckedSingleton() {
    12     }
    13     //公共静态方法获取实例
    14     public static DoubleCheckedSingleton getSingletonInstance() {
    15         //先检查实例是否存在,不存在,在进行同步
    16         if (instance == null) {
    17             //同步块,线程安全的创建实例
    18             synchronized (DoubleCheckedSingleton.class) {
    19                 //再次检查实例是否存在,如果不存在才真正的创建实例
    20                 if (instance == null) {
    21                     instance = new DoubleCheckedSingleton();
    22                 }
    23             }
    24         }
    25         return instance;
    26     }
    27 }

      这里,其实如果private volatile static DoubleCheckedSingleton instance = null; 不用volatile也不会有什么问题,但是这里为什么要用volatile呢?留下一点儿思考的空间。

  • 相关阅读:
    Spring Bean的生命周期
    使用docker安装虚拟机并打开ssh连接
    查看/设置JVM使用的垃圾收集器
    使用Apollo动态修改线上数据源
    java8之lambda表达式
    Java8之Stream
    @Bean 的用法
    Java中的Filter过滤器
    详解tomcat的连接数与线程池
    什么是ClassLoader
  • 原文地址:https://www.cnblogs.com/yangkangIT/p/5913524.html
Copyright © 2011-2022 走看看