zoukankan      html  css  js  c++  java
  • 一天一个设计模式:单例模式

    概念:

      作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例。

    特点:

      1.单例类只能有一个实例

      2.单例类必须创建自己的唯一实例

      3.单例类必须给其他所有对象提供这一实例。

    饿汉式单例类

    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;
        }
    }

    只有在真正需要的时候才会去创建,采用的是时间换空间,由于是线程安全的,会降低访问速度。固可使用双检锁的方式进行优化。

    双重检查加锁

    机制:并不是每次进入getInstance方法都需要同步,而不是先同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一检查,进入同步块后,在检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查,这样一来就只需要同步一次,减少了多次在同步情况下进行判断所浪费的时间。

    双检锁的关键是,使用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关键字会屏蔽虚拟机的一些代码优化,所以运行效率并不高,所有其实应该尽量避免使用双检锁的方式来实现单例。

    懒加载模式(内部类形式)

    什么是内部类:

      内部类就是指在类里面的类,被static修饰的内部类,称为类级内部类,无static修饰的内部类称为对象级内部类。

      类级内部类是外部类的static部分,它的对象与外部类对象间并没有依赖关系,因此可以直接创建。而对象级内部类的实例,是绑定在外部对象的实例中的。

      类级内部类中,可以定义静态方法,在静态方法中只能够引用外部类中的静态成员或者成员变量。

      类级内部类相当于外部类的成员,只有在第一次被使用的时候才会加载。

    多线程缺省同步锁的相关知识:

      在下面操作下,jvm会自动为该操作进行同步,以避免出现并发安全问题。

      1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据。

      2.访问final字段时,

      3.在创建线程前创建对象时,

      4.线程可以看见它将要处理的对象时。

    综上的解决思路:

      采用静态初始化器的方式通过jvm来保证线程的安全性,采用类级内部类的方式实现延迟加载。

    public class Singleton {
        
        private Singleton(){}
        /**
         *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
         *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
         */
        private static class SingletonHolder{
            /**
             * 静态初始化器,由JVM来保证线程安全
             */
            private static Singleton instance = new Singleton();
        }
        
        public static Singleton getInstance(){
            return SingletonHolder.instance;
        }
    }

      在getInstance第一次被调用时,会读取内部类的instance,这时,类级内部类完成初始化,从而创建单例,实现了延时加载,由于是静态成员,只有在类加载的时候才加载一次,且通过jvm的缺省方式实现了线程安全问题。

    单例类枚举:

      单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。

    public enum Singleton {
        /**
         * 定义一个枚举的元素,它就代表了Singleton的一个实例。
         */
        
        uniqueInstance;
        
        /**
         * 单例可以有自己的操作
         */
        public void singletonOperation(){
            //功能处理
        }
    }

      使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。

  • 相关阅读:
    集合框架之Map学习
    集合框架之Set学习
    解决word2016鼠标每点击一下就出现一个保存的圆圈
    装饰者模式
    IO的学习与使用
    Enumeration的学习
    在html页面中引入公共的头部和底部
    WEB-INF下资源访问问题
    给自己立一个flag
    elementui 日期选择值格式
  • 原文地址:https://www.cnblogs.com/lilinzhiyu/p/9883574.html
Copyright © 2011-2022 走看看