• Loubobooo
    2019-07-23
    使用枚举来实现单例模式,具体代码如下:public class SinletonExample5 {
        private static SinletonExample5 instance = null;

        // 私有构造函数
        private SinletonExample5(){
        }

        public static SinletonExample5 getInstance(){
            return Sinleton.SINLETON.getInstance();
        }

        private enum Sinleton{
            SINLETON;

            private SinletonExample5 singleton;

            // JVM保证这个方法只调用一次
            Sinleton(){
                singleton = new SinletonExample5();
            }

            public SinletonExample5 getInstance(){
                return singleton;
            }
        }
    }
    展开

    作者回复: 很赞!这是一种懒加载模式的枚举实现。

     1
     26
  • 豆泥丸
    2019-07-23
    最安全的枚举模式,反射和序列化都是单例。

    作者回复: 对的,我们之前序列化优化这一讲中的问答题就是与枚举实现单例相关,《Effective Java》作者也是强烈推荐枚举方式实现单例。

    
     13
  • 行者
    2019-07-23
    枚举也是一种单例模式,同时是饿汉式。
    相比Double Check,以内部类方式实现单例模式,代码简洁,性能相近,在我看来是更优的选择。

    作者回复: 也可以使用枚举实现懒汉模式,可以根据本讲中的使用内部类方式实现懒加载。

    
     4
  • -W.LI-
    2019-07-23
    枚举底层实现就是静态内部类吧

    作者回复: 对的,枚举是一种语法糖,在Java编译后,枚举类中的枚举会被声明为static,接下来就跟我们文中讲的一样了。

    
     4
  • 我又不乱来
    2019-07-23
    枚举天生就是单例,但是不清楚这么实现。
    注册式单例,spring应该是用的这种。这个也不太清楚,超哥有机会讲一下spring的实现方式和枚举方式实现的单例。谢谢😁

    作者回复: Spring中的bean的单例虽然是一种单例效果,但实现方式是通过容器缓存实现,严格来说是一种享元模式。

    
     4
  • Jxin
    2019-07-23
    1.可能大部分同学都知道,但为了少部分同学,我在老师这个单例上补个点。其它线程空指针异常确实是指令重排导致的,但其原因还有一个。加锁并不能阻止cpu调度线程执行体,所以时间片还是会切的(假设单核),所以其他线程依旧会执行锁外层的if(),并发情况下就可能拿到仅赋值引用,未在内存空间存储数据的实例(null实例),进而空指针。
    2.给老师的代码补段骚的:
    // 懒汉模式 + synchronized 同步锁 + double-check
    public final class Singleton {
        private static validate Singleton instance = null;// 不实例化
        public List<String> list;//list 属性
        private Singleton(){
          list = new ArrayList<String>();
        }// 构造函数
        public static Singleton getInstance(){// 加同步锁,通过该函数向整个系统提供实例
            Singleton temp = instance;
            if(null == temp){// 第一次判断,当 instance 为 null 时,则实例化对象,否则直接返回对象
              synchronized (Singleton.class){// 同步锁
                 temp = instance;
                 if(null == temp){// 第二次判断
                    temp = new Singleton();// 实例化对象
                    instance = temp;
                 }
              }
            }
            return instance;// 返回已存在的对象
        }
    }
    用临时变量做方法内数据承载(相对于validate修饰的属性,可以减少从内存直接拷贝数据的次数),最后用instance接收临时变量时,因为是validate修饰,所以也不会有指令重排。所以前面临时变量的赋值操作已经完成,这样instance就必然是赋值好的实例。(如有错误请老师指出,仅个人理解的骚操作)

    3.极限编程试试就好,业务代码还是尽量优先保证可读性,只有在有性能需求时再采用影响可读性的性能优化。我的这种骚写法和老师的内部类,这种看起来需要想那么一下的东西尽量避免,简单才是王道。
    展开

    作者回复: 虽然有点绕,还是值得表扬的。我们还是鼓励简单易懂的编程风格。

     1
     3
  • Zed
    2019-07-23
    容器类管理

    class InstanceManager {
        private static Map<String, Object> objectMap = new HashMap<>();
        private InstanceManager(){}
        public static void registerService(String key,Object instance){
            if (!objectMap.containsKey(key)){
                objectMap.put(key,instance);
            }
        }
        public static Object getService(String key){
            return objectMap.get(key);
        }
    }
    展开

    作者回复: Spring中bean的单例就是使用容器来实现的,便于管理。

     2
     2
  • 我知道了嗯
    2019-07-23
    枚举实现单例

    作者回复: 对的

    
     2
  • 张三丰
    2019-10-22
    如果把这个成员变量的static去掉,在多线程情况下就可能创建多个实例,单线程没问题。老师,这么理解没问题吧?


    // 饿汉模式
    public final class Singleton {
        private Singleton instance=new Singleton();// 自行创建实例
        private Singleton(){}// 构造函数
        public static Singleton getInstance(){// 通过该函数向整个系统提供实例
            return instance;
        }
    }
    展开

    作者回复: 这个没法调用的哦,静态方法无法调用非静态成员变量。

    
     1
  • QQ怪
    2019-07-23
    这一节虽然都懂,但是评论区补充的我还是第一次见到,get到了,有收获,哈哈

    作者回复: 互相学习,共同进步

    
     1
  • 楊威
    2020-01-19
    爱是一道光
    
    
  • 某、 
    2019-12-19
    用CAS的方式实现单例模式
    
    
  • ylw66
    2019-10-28
    👍,看过的讲singleton最好的文章
    
    
  • 风轻扬
    2019-09-14
    老师,您在内部类实现单例的例子的中。在私有构造方法中,手动new了一个ArrayList集合。在后面的方法中并没有使用这个list,这个list实例是干嘛用的

    作者回复: 可以忽略,这个属性在这里只是用作重排序的问题

    
    
  • godtrue
    2019-09-12
    不错,以为自己懂了的东西,其实深挖之后发现还有许多自己从未想到的宝贝。
    感谢,老师及其他同学的分享!
    内部类实现的懒汉模式,代码简单性能优越也不存在线程安全问题,值得尝试。
    
    
  • 风轻扬
    2019-09-11
    老师。枚举单例可以防止反射攻击、序列化攻击。但是,我们要获取的实例化对象怎么防止暴力反射呢?我现在的做法是在实例化对象的私有构造器中加判断,如果暴力反射,直接抛出运行异常。老师有没有好的办法?百思不得其解

    作者回复: 可以通过反序列化对象白名单来控制运行反序列哪些对象,这种方式需要重写resolveClass 方法,具体参考09讲:

    极客时间版权所有: https://time.geekbang.org/column/article/99774

    
    
  • 疯狂咸鱼
    2019-09-01
    老师,赵饿汉模式的写法,java 中static变量可以保证线程安全了?什么情况下可以呢

    作者回复: 不行,这里指的是加载时是线程安全的。

    
    
  • rong
    2019-08-15
    大佬,请教个问题,“这种方式实现的单例模式,在类加载阶段就已经在堆内存中开辟了一块内存,用于存放实例化对象,所以也称为饿汉模式。”, 饿汉模式下,类加载阶段,不会执行static代码块和成员变量,初始化阶段的时候才会执行。所以,类加载阶段,不会开辟内存吧?

    作者回复: 对的,已纠正

    
    
  • 路西法
    2019-08-12
    // 懒汉模式 + synchronized 同步锁 + double-check
    public final class Singleton {
        private volatile static Singleton instance= null;// 不实例化
        public List<String> list = null;//list 属性
        private Singleton(){
          list = new ArrayList<String>();
        }// 构造函数
        public static Singleton getInstance(){// 加同步锁,通过该函数向整个系统提供实例
            if(null == instance){// 第一次判断,当 instance 为 null 时,则实例化对象,否则直接返回对象
              synchronized (Singleton.class){// 同步锁
                 if(null == instance){// 第二次判断
                    instance = new Singleton();// 实例化对象
                 }
              }
            }
            return instance;// 返回已存在的对象
        }
    }



    这里不需要加 volatile

    synchronized 遵循 happens before 原则,即 在 synchronized 块里的写,对后续其它线程的读是可见的。
    展开

    作者回复: 这里的volatile 是用来防止重排序

    
    
  • 莫观生
    2019-08-04
    超哥,jvm要在代码被执行一定次数之后才会触发即时编译,也就是getInstance方法需要触发多次才会触发即时编译导致指令重排,但是getInstance被执行一次后单例对象就已经被初始化了,理论上是不应该出现指令重排的问题?求解惑

    作者回复: 即时编译并非运行多次代码才会触发,java将字节码转为机器码时就是即时编译

     1
    
我们在线,来聊聊吧