Design Pattern —— Singleton

Design Pattern —— Singleton   强力推荐枚举和类级内部类方式实现单例模式

单例模式是开发中非常常用的一种模式,简单的说,我们希望一个类永远都只有一个对象。

主要有两个用途:

1.存储一些进程内共享的值(不是很推荐,大部分情况下还是应该用局部变量,互相传递值的方式)

2.任何时候都不变的操作

 

单例模式的实现目前已知的有五种:

1.饿汉式

2.懒汉式

3.双重验证

4.类级内部类

5.枚举

 

一、饿汉式

类加载时就创建好对象,以空间换时间。这样外部调用EagerSingleton.getInstance()时,直接获得这个创建好的对象。

 1 public class EagerSingleton {
 2     private static EagerSingleton instance = new EagerSingleton();
 3     /**
 4      * 私有默认构造子
 5      */
 6     private EagerSingleton(){}
 7     /**
 8      * 静态工厂方法
 9      */
10     public static EagerSingleton getInstance(){
11         return instance;
12     }
13 }

优点:节省运行时间

缺点:占用空间

 

二、懒汉式

类加装时不创建对象,直到需要使用时才创建。

 1 public class LazySingleton {
 2     private static LazySingleton instance = null;
 3     /**
 4      * 私有默认构造子
 5      */
 6     private LazySingleton(){}
 7     /**
 8      * 静态工厂方法
 9      */
10     public static synchronized LazySingleton getInstance(){
11         if(instance == null){
12             instance = new LazySingleton();
13         }
14         return instance;
15     }
16 }

优点:节省空间,如果一直不用,就不会创建

缺点:每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。且是由于是线程安全的,所以会降低整体的访问速度

 

三、双重验证

双重验证的单例模式,是懒汉式单例的一种优化方式。既实现线程安全,又能够使性能不受很大的影响。

定义:

1.先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查;

2.进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。

这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

  “双重检查加锁”机制的实现会使用关键字volatile,简单的说:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

 1 public class Singleton {
 2     private volatile static Singleton instance = null;
 3     private Singleton(){}
 4     public static Singleton getInstance(){
 5         //先检查实例是否存在,如果不存在才进入下面的同步块
 6         if(instance == null){
 7             //同步块,线程安全的创建实例
 8             synchronized (Singleton.class) {
 9                 //再次检查实例是否存在,如果不存在才真正的创建实例
10                 if(instance == null){
11                     instance = new Singleton();
12                 }
13             }
14         }
15         return instance;
16     }
17 }

双重验证的方式虽然看上去很美,但是是不被推荐使用的。具体volatile的使用也是一个比较大的课题,有一篇非常好的文章推荐http://www.cnblogs.com/dolphin0520/p/3920373.html

 

四、类级内部类实现的方式。

那我们能不能想到一个办法,既让第一次使用时才创建对象,又解决线程安全呢。

类级内部类的特点:

1.类级内部类的对象和外部类对象没有依赖关系

2.类级内部类中可以有静态方法,此静态方法只能调用外部类的静态方法和成员变量

3.类级内部类只有在第一次使用时才会加载

 

JVM在有些时候会隐式的去执行同步操作:

1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时

2.访问final字段时

3.在创建线程之前创建对象时

4.线程可以看见它将要处理的对象时

 

实现线程安全:可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。

实现延迟加载:采用类级内部类,在这个类级内部类里面去创建对象实例。只要不使用到这个类级内部类,那就不会创建对象实例。

 

 1 public class Singleton {
 2     
 3     private Singleton(){}
 4     /**
 5      *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
 6      *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
 7      */
 8     private static class SingletonHolder{
 9         /**
10          * 静态初始化器,由JVM来保证线程安全
11          */
12         private static Singleton instance = new Singleton();
13     }
14     
15     public static Singleton getInstance(){
16         return SingletonHolder.instance;
17     }
18 }

 

 五、枚举实现单例

《effectJava》一书中重点推荐的实现单例的方式

 

 1 public enum Singleton {
 2     /**
 3      * 定义一个枚举的元素,它就代表了Singleton的一个实例。
 4      */
 5     
 6     uniqueInstance;
 7     
 8     /**
 9      * 单例可以有自己的操作
10      */
11     public void singletonOperation(){
12         //功能处理
13     }
14 }

枚举单例,简洁,提供序列化机制,防止多实例化,防止反射,是实现单例的最佳方案。

 

参考资料

http://www.cnblogs.com/dolphin0520/p/3920373.html  violate详解

http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html  java与模式之单例模式

《effectJava》

posted @ 2015-12-10 23:08  xerrard  阅读(233)  评论(0编辑  收藏  举报