• 单例模式


    单例模式的八种方式

    • 饿汉式(静态常量)
    • 饿汉式(静态代码块)
    • 懒汉式(线程不安全)
    • 懒汉式(线程安全,同步方法)
    • 懒汉式(线程安全,同步代码块)
    • 双重检查
    • 静态内部类
    • 枚举

    八种方式详解

    饿汉式(静态常量)

    步骤

    1. 构造函数私有化
    2. 类的内部创建对象
    3. 向外暴露一个静态的公共方法

    代码实现

    
    public class BaseSingleton {
        //加载该类时,单例就会自动被创建
        private static BaseSingleton baseSingleton = new BaseSingleton();
    
        //构造函数设置有私有函数,禁止他人创建实例
        private BaseSingleton() {
    
        }
    
        //通过静态方法获得创建的单例
        public static BaseSingleton newInstance() {
            return baseSingleton;
        }
    }
    
    

    优缺点

    • 优点:写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
    • 缺点:在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费

    饿汉式(静态代码块)

    步骤

    1. 构造函数私有化
    2. 类的内部声明静态变量
    3. 静态代码块实现对象的初始化
    4. 向外暴露一个静态的公共方法

    代码实现

    代码实现

    public class BaseSingleton {
        //加载该类时,单例就会自动被创建
        private static BaseSingleton baseSingleton;
    
        static {
            baseSingleton = new BaseSingleton();
        }
    
        //构造函数设置有私有函数,禁止他人创建实例
        private BaseSingleton() {
    
        }
    
        //通过静态方法获得创建的单例
        public static BaseSingleton newInstance() {
            return baseSingleton;
        }
    }
    
    

    优缺点

    • 和静态常亮一样,写法简单,在类装载的时候就完成了实例化,避免了线程同步的问题
    • 在类装载的时候就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费

    懒汉式(线程不安全)

    步骤

    1. 构造函数私有化
    2. 类的内部声明静态变量
    3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回

    代码实现

    public class LazySingleton {
    
        //类加载时,先不自动创建实例,将单例的引用赋值为null
        private static LazySingleton lazySingleton = null;
    
        //构造函数私有化,防止他人创建实例
        private LazySingleton() {
        }
    
        //需要时调用newInstance 创建实例
        public static LazySingleton newInstance() {
            //先判断是否为空,避免重复创建
            if (lazySingleton == null) {
                lazySingleton = new LazySingleton();
            }
            return lazySingleton;
        }
    }
    
    

    优缺点

    • 起到了懒加载的作用,但是只能在单线程下使用
    • 如果在多线程下,一个线程进入了if (lazySingleton == null)中,还未来得及在往下执行,线程被挂起,另一个线程也通过了这个判断语句,这个时候就会产生多个实例,所以多线程下不可以用这种方式

    懒汉式(线程安全,同步方法)

    步骤

    1. 构造函数私有化
    2. 类的内部声明静态变量
    3. 向外暴露一个同步(synchronized)的静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建,存在则直接返回

    代码实现

    public class LazySingleton {
    
        //类加载时,先不自动创建实例,将单例的引用赋值为null
        private static LazySingleton lazySingleton = null;
    
        //构造函数私有化,防止他人创建实例
        private LazySingleton() {
        }
    
        //需要时调用newInstance 创建实例,方法同步synchronized
        public static synchronized LazySingleton newInstance() {
            //先判断是否为空,避免重复创建
            if (lazySingleton == null) {
                lazySingleton = new LazySingleton();
            }
            return lazySingleton;
        }
    }
    
    
    

    优缺点

    • 解决了线程不安全的问题
    • 效率低下,每个线程在想获得实例的时候,执行newInstance()方法都要进行同步,而这个方法只执行一次实例化代码就够了,后面想要获得该类的实例,直接return就可以,方法进行同步效率过于低下

    懒汉式(线程安全,同步代码块)

    步骤

    1. 构造函数私有化
    2. 类的内部声明静态变量
    3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,不存在则创建(使用同步代码块),存在则直接返回

    代码实现

    public class LazySingleton {
        //类加载时,先不创建对象,单例的引用先赋值未null
        private static LazySingleton LazySingleton = null;
    
        //构造函数私有化,防止他人创建实例
        private LazySingleton() {
        }
    
        public static LazySingleton newInstance2() {
            synchronized (LazySingleton.class) {
                if (LazySingleton == null) {
                    LazySingleton = new LazySingleton();
                }
            }
            return LazySingleton;
        }
    
    }
    

    优缺点

    • 这种方式和同步方法的方式类似,只是为了减少同步的范围将同步方法缩小到具体的实例方法处,效率问题没有降低

    双重检查

    步骤

    1. 构造函数私有化
    2. 类的内部声明静态变量
    3. 向外暴露一个静态的公共方法,在调用该方法时,判断对象是否存在,存在则直接返回,不存在的话,加入锁代码,锁代码中再次判断对象是否存在,不存在的话再进行对象的实例化

    代码实现

    /**
     * @Classname LazySingleton
     * @Description 双重校验锁,在同步锁的基础上,添加一层if判断,若单例已经创建,则不需要执行加锁操作就可以获取实例,从而提高性能。
     * @Date 19-2-1 下午2:34
     * @Created by yujuan
     */
    public class LazySingleton {
        private static LazySingleton LazySingleton;
    
        private LazySingleton() {
        }
    
        public static LazySingleton newInstance() {
            //加入双重校验
            //校验锁1 第一个if
                //作用:若单例已经创建,则直接返回创建的单例对象,无需在进行加锁操作
            if (LazySingleton == null) {
                //校验锁2 第二个if
                    //作用:防止多次创建单例问题
                    //原理:
                        //1、线程A调用newInstance 运行到2位置时,此时线程B也调用了newInstance
                        //2、因线程A还没有执 LazySingleton = new LazySingleton() ,此时lazySingleton为空,因此线程B能突破第一层if判断,运行到1synchronized中的A执行完成
                        //3、当线程A释放同步锁的时候,单例已经创建,LazySingleton不为空
                        //4、此时线程B从1开始位置执行到2时,此时第二层if判断不成立,不会再次创建多余的实例
                synchronized (LazySingleton.class) {
                    if (LazySingleton == null) {
                        LazySingleton = new LazySingleton();
                    }
                }
            }
            return LazySingleton;
    
        }
    }
    
    

    优缺点

    • 线程安全
    • 延迟加载,解决了之前单例模式懒加载效率低下的问题

    静态内部类

    步骤

    1. 构造函数私有化
    2. 声明一个静态内部类,在类的内部完成实例的创建
    3. 向外暴露一个静态的公共方法,提供对象的实例

    代码实现

    /**
     * @Classname InnerClassSingleton
     * @Description 根据静态内部类的特性,同时解决了按需加载,线程安全的问题,同时实现简单
     * 1、在静态内部类中创建单例,在装载该内部类的时候才会去创建单例
     * 2、线程安全,类是由JVM加载,而JVM只会加载一遍,保证只有一个实例
     * @Date 19-2-1 下午2:45
     * @Created by yujuan
     */
    public class InnerClassSingleton {
        //创建静态内部类
        private static class InnerClassSingleton2 {
            //在静态内部类中创建实例
            private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
        }
        //私有构造函数
        private InnerClassSingleton() {
        }
        //延迟加载,按需创建
        public static InnerClassSingleton newInstance() {
            return InnerClassSingleton2.innerClassSingleton;
        }
        //调用过程说明:
            //1、外部调用类的newInstance()
            //2、自动调用InnerClassSingleton2.innerClassSingleton 得到初始化
                //2.1 此时单例类InnerClassSingleton2 得到初始化
                //2.2 而该类在装载&被初始化时,会初始化它的静态域,从而创建单例
                //2.3 由于是静态域,因此只会JVM加载一次,java虚拟机保证的线程的安全性
            //3、最终只创建了一个单例
    }
    
    

    优缺点

    • 采用类装载的机制来保证初始化实例时只有一个线程
    • 静态内部类在InnerClassSingleton类被装载时不会立即实例化,而是在需要的实例化的时候,调用newInstance方法,才会进行对内部静态类的装载,从而完成对Singleton的实例化
    • 类的静态属性只会在第一次加载类的时候j进行初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
    • 避免的线程不安全的问题,利用静态内部类特点实现延迟加载,效率高

    枚举

    步骤

    1.创建一个枚举类,添加枚举来实现单例模式

    代码实现

    public enum enumSingleton {
        INSTANCE;
    }
    
    

    优缺点

    • 避免多线程同步的问题,可以防止反序列化重新创建新的对象

    AtomicReference

    使用AtomicReference 修饰对象,通过CAS的方式实现单例模式 参考阿里云开发者社区

    public class Singleton {
        private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); 
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            for (;;) {
                Singleton singleton = INSTANCE.get();
                if (null != singleton) {
                    return singleton;
                }
    
                singleton = new Singleton();
                if (INSTANCE.compareAndSet(null, singleton)) {
                    return singleton;
                }
            }
        }
    }
    
  • 相关阅读:
    Angular
    Angular
    Angular
    Angular
    Angular
    Angular
    Angular
    springsecurity 源码解读 之 RememberMeAuthenticationFilter
    springsecurity 源码解读之 AnonymousAuthenticationFilter
    springsecurity 源码解读之 SecurityContext
  • 原文地址:https://www.cnblogs.com/jakaBlog/p/11947549.html
Copyright © 2020-2023  润新知