• 单例模式


    定义:

      单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。

    特点:

           1、单例类只能有一个实例。
      2、单例类必须自己自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。

    单例模式的要点:

        1,私有的构造方法

        2,指向自己实例的私有静态引用

        3,以自己实例为返回值的静态的公有的方法

    单例模式分类:

    懒汉式(两种)、饿汉式(两)、静态内部类、枚举、双重校验锁等共七种实现方式。

    实现:

    第一种(懒汉,线程不安全):

    public class Singleton {  
        private static Singleton instance;  
        private Singleton (){}  
      
        public static Singleton getInstance() {  
          if (instance == null) {            //当多条线程到达此处时,可能会出现多条线程都通过if判断,继而创建对象,导致创建单例对象失败
              instance = new Singleton();  
          }  
          return instance;  
        }  
    }  

    第二种(懒汉,线程安全):

    public class Singleton {  
        private static Singleton instance;  
        private Singleton (){}  
        public static synchronized Singleton getInstance() {  //通过加锁,使同时只有一条线程访问到该方法,线程安全,但是效率低下
          if (instance == null) {  
              instance = new Singleton();  
          }  
          return instance;  
        }  
    }  

    第三种(饿汉式)

    public class Singleton {  
        private static Singleton instance = new Singleton();  //类加载就实例化,但是类什么时候加载,这个是不太确定的,大部分情况是调用getInstance()时加载的。
        private Singleton (){}  
        public static Singleton getInstance() {  
          return instance;  
        }  
    }  

    第四种(饿汉式,变种)

    public class Singleton {  
        private static Singleton instance = null;  
        static {                  //根据java的类加载机制,在类加载的时候,静态代码块就运行并进行了实例化,实际上跟第三种是差不多的
          instance = new Singleton();  
        }  
        private Singleton (){}  
        public static Singleton getInstance() {  
          return instance;  
        }  
    }  

    第五种(静态内部类)

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

    当类Singleton被加载时,并不会立刻进行实例化(SingletonHolder未被使用就不会主动加载,就不会实例化,实现lazy loading),可以实现当实例化需要消耗巨大资源时延迟加载,也可以避免主动实例化(目前这个场景还没有想到)

    第六种(枚举)

    public enum Singleton {  //这个模式博主暂时没有搞明白。如果有大佬看到的话可以帮忙指出
        INSTANCE;  
        public void whateverMethod() {  
        }  
    }  

    特点:类似于饿汉式,代码简单,自有序列化。

    2017/12/21

    枚举本身实际上就是一个class

    实际上枚举类型中的每一个枚举(INSTANCE)都是枚举类的一个实例对象。

    枚举的构造方法时私有的。所以可以在一定程度上实现单例

    第七种(双重校验锁)

    public class Singleton {  
        private volatile static Singleton singleton;  //看过一篇博文,说在java1.2之后就可以避免已经实例化后变量无法指向实例的地址值(实例化后会保证变量一定能拿到其指向实例的内存地址)
        private Singleton (){}               //也就是说解决了安全性问题。
        public static Singleton getSingleton() {    //但是也有博文说,在java1.5之后才能正常使用(具体的还没有找到博文比较尴尬)
        if (singleton == null) {  
            synchronized (Singleton.class) {  
              if (singleton == null) {  
                  singleton = new Singleton();  
              }  
            }  
        }  
        return singleton;  
        }  
    }  

    总结:

    有两个问题需要注意:

    1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

    2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

    对第一个问题修复的办法是:

    private static Class getClass(String classname)      
                                             throws ClassNotFoundException {     //这段代码,其实我看不懂。。。。
          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
          
          if(classLoader == null)     
             classLoader = Singleton.class.getClassLoader();     
          
          return (classLoader.loadClass(classname));     
       }     
    }  

     

     对第二个问题的修复方法:

    public class Singleton implements java.io.Serializable {     
       public static Singleton INSTANCE = new Singleton();     
          
       protected Singleton() {     
            
       }     
       private Object readResolve() {   //区别也就是说返回值类型变了,这里我也不太懂。有点懵逼    
                return INSTANCE;     
          }    
    }   

     

     一般想要实现懒加载可以选用第五种方法,双重校验锁也是很好的选择(线程安全)

     

    如果看到我文章的大佬觉得哪里有问题,欢迎您指正。

  • 相关阅读:
    iOS多线程的初步研究(十)-- dispatch同步
    iOS 几种定时器
    iOS 如何计算UIWebView的ContentSize
    iOS 如何计算UIWebView的ContentSize
    iOS: NSTimer使用小记
    iOS: NSTimer使用小记
    使用Objective-C的+(void)initialize初始化static变量
    使用Objective-C的+(void)initialize初始化static变量
    拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录
    拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录
  • 原文地址:https://www.cnblogs.com/lilinzhiyu/p/7922715.html
Copyright © 2020-2023  润新知