zoukankan      html  css  js  c++  java
  • java23种设计模式之一: 单例模式(Singleton Pattern)

    单例模式(Singleton Pattern)是设计模式中比较常用的一种,下面来总结单例模式的知识,包括:

           1、理解什么是单例模式、单例模式有什么优点/缺点、单例模式的应用场景;

           2、再来看看Java单例模式的6种代码实现方式、每种实现方式有什么需要注意的;

           3、后面再来了解Java单例模式其他值得关注的地方,如比较静态方法、以及Java反射、反序列化、垃圾回收的影响等。

    1、什么是单例模式

    1-1、模式理解

     保证一个类仅有一个实例,并提供一个访问它的全局访问点。

           UML结构图:

        

           模式角色:

          一个类使用了单例模式,称该类为单例类,如图中的Singleton。

           单例模式三要点:

          (1)、单例类只能有一个实例

          这是最基本的,真正做到整个系统中唯一并不容易,通常还要考虑反射破坏、序列化/反序列化、对象垃圾回收等问题。

          (2)、单例类必须自己创建自己的唯一实例

          通常给实例构造函数protected或private权限。

          (3)、单例类必须给所有其他对象提供这一实例

          通常定义静态方法getInstance()返回。

    1-2、特点

           优点:

           (1)、提供了对唯一实例的受控访问,避免对资源的多重占用。

           (2)、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。

           (3)、缩小名空间,避免全局变量污染空间,但比类操作更灵活。

           缺点:

           (1)、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

           (2)、 单例类的职责过重,在一定程度上违背了"单一职责原则"。

          因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。 所以也不应过多使用单例模式。

    1-3、应用

    单例模式是一种对象创建型模式,用来编写一个类,在整个应用系统中只能有该类的一个实例对象。

           常见应用场景:

               线程池、缓存、日志、配置文件、打印机/显卡等硬件设备的驱动程序对象等等。

           JDK中的一些应用:

             java.lang.Runtime#getRuntime()

            java.text.NumberFormat#getInstance()

            java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()

    2、单例模式实现示例

    2-1 饿汉式:类初始化时直接创建对象,不存在线程安全问题

    2-1-1、饿汉式(简洁直观)

      Lazy 初始化:否;

           多线程安全:是;

           描述:

        这种方式比较常用,它基于JVM的类加载器机制避免了多线程的同步问题,对象在类装载时就实例化,所以称为饿汉式。

           优点:没有加锁,执行效率会提高。

           缺点:没有Lazy初始化,可能有时候不需要使用,浪费内存。

           代码实例:

    public class Singleton {
     
        private static Singleton instance = new Singleton();
       
        private Singleton (){}
        
        public static Singleton getInstance() {
            return instance;
     
        }

    2-1-2、枚举(最简洁)

       Lazy 初始化:否;

      多线程安全:是;

      描述:

            从Java1.5开始支持enum特性;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。不过,用这种方式写不免让人感觉生疏,这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。

          代码实例

    public enum Singleton {
     
        //定义一个枚举的元素,就代表Singleton实例
        INSTANCE;
     
        /*
        **假如还定义有下面的方法,调用:Singleton.INSTANCE.doSomethingMethod();
        */
     
        public void doSomethingMethod() {
     
        }

    2-1-3、静态代码块饿汉式(适合复杂实例化)

      Lazy 初始化:否;

           多线程安全:是;

    public class Singleton3 {
        private static Singleton3 instance;
    
        static {
            instance = new Singleton3();
        }
    
        private Singleton3(){}
    
        private Singleton3 getInstance(){
            return instance;
        }

    2-2、懒汉式:延迟创建对象

    2-2-1、懒汉式(线程不安全,适用单线程)

        Lazy 初始化:是;

          多线程安全:否;

          描述:

          能够在getInstance()时再创建对象,所以称为懒汉式。这种实现最大的问题就是不支持多线程。因为没有加锁同步。

          代码实例:

    public class Singleton {
     
        private static Singleton instance;
     
        private Singleton (){}
         
        public static Singleton getInstance() {
     
            if (instance == null) {
                instance = new Singleton();
            }
     
            return instance;
        }

    2-2-2、同步方法的懒汉式(同步方法效率低,不推荐)

        Lazy 初始化:是

          多线程安全:是

          描述:

      除第一次使用,后面getInstance()不需要同步;每次同步,效率很低。

        代码实例:

    public class Singleton {
     
        private static Singleton instance;
     
        private Singleton (){}
     
        public static synchronized Singleton getInstance() {
     
            if (instance == null) {
                instance = new Singleton();
            }
     
            return instance;
        }

    2-2-3、双重校验锁(线程安全,可用,适用于多线程)     

       Lazy 初始化:是;

          多线程安全:是;

          描述:

          这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

          实例变量需要加volatile 关键字保证易变可见性,JDK1.5起才可用。

          代码实例:

    public class Singleton {
     
        private volatile static Singleton singleton;
     
        private Singleton (){}
     
        public static Singleton getSingleton() {
     
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                     }
                 }
            }
     
            return singleton;
        }

    2-2-4、静态内部类(推荐)

        Lazy 初始化:是;

          多线程安全:是;

          描述:

          同样利用了JVM类加载机制来保证初始化实例对象时只有一个线程,静态内部类SingletonHolder 类只有第一次调用 getInstance 方法时,才会装载从而实例化对象。

       静态内部类不会自动随着外部类的加载和初始化而初始化,它是单独加载和初始化的

          代码实例:

    public class Singleton {
     
        private static class SingletonHolder {
     
           private static final Singleton INSTANCE = new Singleton();
        }
     
        private Singleton (){}
     
        public static final Singleton getInstance() {
     
            return SingletonHolder.INSTANCE;
        }

     

    2-3、小结

         以上几种单例实现方式,不是线程安全的不能用,至于是否需要延时加载,看情况而定。

          一般情况下,使用最基本、最简单的第一种饿汉式就行了(JDK中有不少使用该种方式),需要延时加载的使用静态内部类方式,需要高安全性的可以使用枚举方式。

  • 相关阅读:
    fenby C语言 P32
    fenby C语言 P31 使用数组的指针
    fenby C语言 P30
    fenby C语言 P29
    fenby C语言 P28
    fenby C语言 P27使用指针
    fenby C语言 P25
    fenby C语言 P26
    fenby C语言P24
    Python学习之路:通过socket实现处理多个连接
  • 原文地址:https://www.cnblogs.com/lc0605/p/10794607.html
Copyright © 2011-2022 走看看