工作中的点点滴滴单例的使用

背景:

  最近上线的交易系统已经大面积推广使用了,然后首页商品信息为了加载速度快一些,我把商品都缓存在了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呢?留下一点儿思考的空间。

原文地址:https://www.cnblogs.com/yangkangIT/p/5913524.html