• 设计模式之单例模式


    1.饿汉模式

    //饿汉模式
    public class Singleton1 {
        private static Singleton1 singleton1 = new Singleton1();  //饿汉嘛,很着急,所以在类加载时就进行初始化
    
        private Singleton1() {
        }
    
        public static Singleton1 getSingleton1() {
            return singleton1;
        }
    }

    饿汉模式是线程安全的,因为 private static Singleton1 singleton1 = new Singleton1(); 语句是在类加载时完成的,具体是在类加载的初始化阶段时的<client>方法中进行的。

    2.懒汉模式

    //懒汉模式
    public class Singleton2 {
        private static Singleton2 singleton2;
    
        private Singleton2() {
        }
    
        public static synchronized Singleton2 getSingleton2() {      //懒汉嘛,很懒,所以啥也不考虑,直接给方法上加synchronized来解决问题(并发时效率很低)
            if (singleton2 == null) {
                singleton2 = new Singleton2();
            }
            return singleton2;
        }
    }

    这种懒汉模式是线程安全的,在这里只有获取到Class的对象锁才会进入到方法。

    3.双重检测模式

    //双重检测模式(可以理解为比懒汉勤快的一种模式。。。)
    public class Singleton3 {
        private static volatile Singleton3 singleton3;
    
        private Singleton3() {
        }          
    
        public static Singleton3 getSingleton3() {               //不在方法上加synchronized,
            if (singleton3 == null) {            //先判断是否null,为null时才回去加锁
                synchronized (Singleton3.class) {    //这是只有在单例还没有被初始化时才需要进行加锁
                    if (singleton3 == null) {      //再一次进行检测,这里主要是因为可能在对象还没初始化时,可能已经有若干个线程已经加入到监视器锁中了,所以这里还要进行一次检测,防止产生多个实例
                        singleton3 = new Singleton3();
                    }
                }
            }
            return singleton3;
        }
    }

    (1)在使用volatile后为线程安全的,volatile用来禁止指令重排序

    (2)为什么要使用volatile关键字

      由于 singleton3 = new Singleton3();是分为三步来进行的

      ①为对象分配内存空间

      ②初始化对象

      ③将对象复制给引用

      由于代码在执行过程可能出现指令重排序,那么执行步骤可能会变成①-③-②,那么当线程A完成①③操作,B线程进入方法判断不为空,将会获取到这个没有初始化完成的对象。

      而使用volatile后禁止这三条指令的重排序。保证在singleton3在不为空是对象已经被初始化。

    4.静态内部类

    //静态内部类
    public class Singleton4 {
        private Singleton4() {
        }
    
        private static class SingletonHolder {            //在静态内部类中去初始化单例
            private static Singleton4 singleton4 = new Singleton4();
        }
    
        public static Singleton4 getSingleton4() {        
            return SingletonHolder.singleton4;      //通过静态内部类来获取
        }
    }

    (1).该实现方式是线程安全的。首先我们看看java中类的初始化时机

      其中之一为:在调用类读取或设置一个类的静态字段的时候。

          所以只有我们在第一次调用getSingleton4()方法时,SingletonHolder才会被初始化。

      

    关于以上代码可在https://github.com/LiWangCai/blogRelated中获取

  • 相关阅读:
    记录Spark 笛卡尔积问题
    接口和继承的区别
    SpringMVC使用可以访问静态资源,但是导致Controller访问失败
    Redis获取缓存异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
    Spring MVC RedirectAttributes取值方法
    Resource interpreted as Stylesheet but transferred with MIME || DevTools failed to parse SourceMap:
    web.xml中filter加载顺序出现的问题
    kafka 与 rocketmq
    从小到大的部署架构
    SSH框架的简单实现
  • 原文地址:https://www.cnblogs.com/liwangcai/p/10750835.html
Copyright © 2020-2023  润新知