java设计模式一单例模式

匿名 (未验证) 提交于 2019-12-02 21:53:52
  • 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
  • 单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1. 需要频繁实例化然后销毁的对象。   2. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。   3. 有状态的工具类对象。  4. 频繁访问数据库或文件的对象。
  • 应用场景举例:
1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。  2. 网站的计数器,一般也是采用单例模式实现,否则难以同步。  3. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。  4. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 
  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
public class EagerSingleton {     private static EagerSingleton instance = new EagerSingleton();     /**      * 私有默认构造子      */     private EagerSingleton(){}     /**      * 静态工厂方法      */     public static EagerSingleton getInstance(){         return instance;     } }
  • 上面的例子中,在这个类被加载时,静态变量instance会被初始化,此时类的私有构造子会被调用。这时候,单例类的唯一实例就被创建出来了。

  • 饿汉式其实是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是在装载类的时候就创建对象实例。

  • 饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
public class LazySingleton {     private static LazySingleton instance = null;     /**      * 私有默认构造子      */     private LazySingleton(){}     /**      * 静态工厂方法      */     public static synchronized LazySingleton getInstance(){         if(instance == null){             instance = new LazySingleton();         }         return instance;     } }
  • 上面的懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。
  • 懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例

  • 懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间

  • 由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式实现呢?

  • “双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
public class Singleton {     private volatile static Singleton instance = null;     private Singleton(){}     public static Singleton getInstance(){         //先检查实例是否存在,如果不存在才进入下面的同步块         if(instance == null){             //同步块,线程安全的创建实例             synchronized (Singleton.class) {                 //再次检查实例是否存在,如果不存在才真正的创建实例                 if(instance == null){                     instance = new Singleton();                 }             }         }         return instance;     } }
  • 这种实现方式既可以实现线程安全地创建实例,而又不会对性能造成太大的影响。它只是第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。
  • volatile关键字的含义是:被其所修饰的变量的值不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存来实现,从而确保多个线程能正确的处理该变量。该关键字可能会屏蔽掉虚拟机中的一些代码优化,所以其运行效率可能不是很高,所以,一般情况下,并不建议使用双重加锁机制,酌情使用才是正理!
  • 饿汉式会占用较多的空间,因为其在类加载时就会完成实例化,而懒汉式又存在执行速率慢的情况,双重加锁机制呢?又有执行效率差的毛病,有没有一种完美的方式可以规避这些毛病呢?
  • 貌似有的,就是使用类级内部类结合多线程默认同步锁,同时实现延迟加载和线程安全。
public class ClassInnerClassDanli {     public static class DanliHolder{         private static ClassInnerClassDanli dl = new ClassInnerClassDanli();     }     private ClassInnerClassDanli(){}     public static ClassInnerClassDanli getInstance(){         return DanliHolder.dl;     } }
  • 如上代码,所谓类级内部类,就是静态内部类,这种内部类与其外部类之间并没有从属关系,加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,有效实现了懒加载(延迟加载),至于同步问题,我们采用和饿汉式同样的静态初始化器的方式,借助JVM来实现线程安全。

  • 其实使用静态初始化器的方式会在类加载时创建类的实例,但是我们将实例的创建显式放置在静态内部类中,它会导致在外部类加载时不进行实例创建,这样就能实现我们的双重目的:延迟加载和线程安全。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!