zoukankan      html  css  js  c++  java
  • 设计模式笔记(一):Singleton 设计模式

    今天开始学习设计模式,借此机会学习并整理学习笔记。

    设计模式是一门不区分语言的课程,什么样的编程语言都可以用到设计模式。如果说java语法规则比作武功招式的话,那么设计模式就是心法。

    设计模式共有23种,常见的19种,最常用的9-10种。

    设计模式分三种类型:创建型、结构型、行为型;

    其中创建型包含单例设计模式、工厂模式、抽象工厂模式、原型模式、建造者模式;结构型包含代理模式、装饰器模式、适配器模式、外观模式、组合模式、享元模式、桥梁模式;行为型包含:策略模式、责任链模式、命令模式、中介者模式、模板方法模式、迭代器模式、访问者模式、观察者模式、解释器模式、备忘录模式、状态模式。

    好了,凑字完毕,开始今天的学习整理

    Singleton设计模式,就是单例设计模式。

    单例设计模式有句顺口溜:单例一实例,私有构造器

    单例设计模式分为饿汉式和懒汉式。

    饿汉式是用户没有请求你的要求时,已经把这个实例提前创建出来了;

    懒汉式则是需只有外部需要调用getInstance的时候,才回去初始化这个单例。

    这样就有一个区别:

    饿汉式:用空间换时间 

    懒汉式:用时间换空间

    下面是具体代码实现:


     singleton 单例设计模式
     1:饿汉式
    饿汉式优缺点:
    优点: 实现简单,没有多线程同步问题(默认条件下是线程安全的)
    缺点:当类加载SingletonTest的时候,会初始化static的instance,静态变量被创建并分配内存空间,从此,这个static的instance对象便一直占着此段内存(即使你并没有使用该实例,当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
    简单来说 用空间换时间

    为什么在饿汉式中不讨论线程安全问题:
     因为类里面的静态实例都是由类加载器来负责初始化的,类加载器classLoader在出现类名的时候就会开始工作,把类的静态实例全部初始化,而且只初始化一次,所以一定是安全的,

    public class Singleton {
        //静态一实例  将自身实例化对象设置为一个属性,并用static final修饰
        private static Singleton instance  = new Singleton();

        //私有构造器
        private Singleton() {
            
        }
        
        //外部通过静态方法返回该实例
        public static Singleton getInstance() {
            return instance;
        }     
    }


    2:懒汉式
    懒汉式优缺点:
    优点:实现起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance()方法第一次调用的时,初始化instance变量,并分配内存空间。在某些条件下这种方式会节省内存。

    缺点:在多线程环境中这种方法是完全错误的,根本不能保证单例的状态 需要添加synchronized(锁)


    public class Singleton2 {
        //对象赋予null值 或者 不赋值  
        private static Singleton2 instance2;

        //私有构造器
        prviate Singleton2() {
            
        }
        
        //外部获取实例对象  此时为了线程安全 需要使用synchronized(锁)
        public static synchronized Singleton2 getInstance2() {
            if(instance2 == null) {
                instance2 = new Singleton2();
            }
            return instance2;
        }       
    }


    3:如何兼顾饿汉式与懒汉式的优点:
     第一种:使用静态内部类的方法

    优点:外部类加载时不予要立即加载内部类,内部类不被加载就不会初始化instance 故而占用内存。当Singleton 第一次加载时,并不需要去加载Singleton3handler只有当getInstance()方法第一次被 调用时,才会去初始化Instance,第一次调用getInstance()方法会导致虚拟机加载在SingletonHangdler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟单例的实例化,

    public class Singleton3 {
            //构建静态内部类
            private static class Singleton3Handler{
                private static Singleton3 instance3 = new Singleton3();
            }
            //私有构造器
            private  Singleton3() {
                
            }
            //外部获取对象
            public static Singleton3 getInstance() {
                return Singleton3.Singleton3Handler.instance3;
            }
    }


    第二种:使用枚举方式  
    优点: 线程是安全的


        public enum Singleton4{
            INSTANCE;
            public void method() {
                //TODO
            }
        }
     在任何情况下,它都是一个单例 我们可以直接 Singleton4.INSTANCE 引用

    第三种:使用DCL模式     需要使用Volatile 关键字
    双重锁懒汉式( Double Check Lock  简称 DCL )
    优点:只有对象需要被使用的时候才创建,第一次判断instance21 == null 为了避免非必要枷锁,当第一次加载时才对实例进行枷锁在实例化。这样就可以节约内存空间,有可以保证线程安全。

    缺点:但是,由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况。

    比如:  
          instance21  = new Singleton2;
          这个步骤,其实在jvm里面的执行分为三步:
          1:在堆内开辟内存空间
          2;在堆内存中实例化Singleton2里面的各个参数
          3:  把对象指向堆内存
          但是这个缺点在jdk1.6以后 只需要定义 为: private volatile  static Singleton5 instance5 = null;  就可以
          解决此问题  。  volatile确保instance每次在均在主内存中读取,这样虽然会牺牲一点效率。

    public class Singleton5{
        // 对象一实例
        private volatile static Singleton5 instance5;
        //私有构造器
        private Singleton5() {
            
        }
        //DCL懒汉式
        public static Singleton5 getInstance() {
            if(instance5==null) {
                synchronized(Singleton5.class){
                    if(instance5 == null) {
                        instance5 = new Singleton5();
                    }
                }
            }
            return instance5;
        }
    }

    需要注意的是:

      需要延迟加载的情况下使用第一、第二种;

      需要防止序列化问题、反射攻击使用第三种。

    才疏学浅,如有错误,恳请指教!

  • 相关阅读:
    由老赵反对青鸟想到的——关于自学编程的讨论
    蛙蛙推荐:《代码大全》第45章读书笔记
    大家来找错自己写个正则引擎(二)构建抽象模式树
    大家来找错自己写个正则引擎(五)检查表及总结
    大家来找错自己写个正则引擎(一)概要介绍
    大家来找错自己写个正则引擎(三)构建正则解析树及分词
    蛙蛙推荐:《代码大全》1至3章读书笔记
    sql for xml path用法(转) dodo
    sql语句总结一 dodo
    System.Management.ManagementException: 访问遭到拒绝的解决方案 dodo
  • 原文地址:https://www.cnblogs.com/jiangliushibuzhuan/p/11417937.html
Copyright © 2011-2022 走看看