zoukankan      html  css  js  c++  java
  • 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》

  • 相关阅读:
    什么叫委托
    什么是继承
    什么叫多态
    委托的了解
    什么是数组
    工作记录之 oracle去重的三个方法
    实例分析J2ME网络编程的两种方法
    在无线J2ME设备上实现超文本传输协议
    java与C、C++进行通信的一些问题
    如何配置Wiindows live writer
  • 原文地址:https://www.cnblogs.com/xerrard/p/5022723.html
Copyright © 2011-2022 走看看