美文网首页并发编程
线程安全的单例模式

线程安全的单例模式

作者: xiaolyuh | 来源:发表于2019-08-20 20:36 被阅读12次

    懒汉式

    加方法锁

    public class Singleton {
        private static Singleton singleton = null;
    
        private Singleton() {
        }
    
        public static synchronized Singleton getInstance() {
            if (Objects.isNull(singleton)) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    
    1. 直接在 getInstance() 方法加锁,但是加锁的范围太大,性能低下

    双重检查锁定

    public class Singleton {
        private static volatile Singleton singleton = null;
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            // 1
            if (Objects.isNull(singleton)) {
                synchronized(Singleton.class) {
                    if (Objects.isNull(singleton)) {
                      // 2  
                      singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }
    

    对象需要加volatile 关键字,主要是防止指令重排序。singleton = new Singleton();方法在执行的时候有三个指令:

    memory = allocate();  // 1:分配对象的内存空间
    ctorInstance(memory); // 2:初始化对象
    instance = memory;  // 3:设置instance指向刚分配的内存地址
    

    当线程A获取到锁,执行初始化的时候发生了指令重排,1->3->2。当2还没有被执行时,线程B执行到代码标记1的位置,这时判断到对象不为空,直接返回该对象,但是这个时候该对象可能还并没有完成初始化,导致线程B在执行过程中抛错。

    静态内部类

    public class Singleton {
        private static class LazyHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return LazyHolder.INSTANCE;
        }
    }
    

    既实现了线程安全,又避免了同步带来的性能影响。JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在 执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。使用这种方式,我们是允许new Singleton();过程发生指令重排的。

    使用枚举的形式

    public class EnumSingleton {
        private EnumSingleton() {
        }
    
        public static EnumSingleton getInstance() {
            return Singleton.INSTANCE.getInstance();
        }
    
        private enum Singleton {
            INSTANCE(new EnumSingleton());
            private EnumSingleton singleton;
    
            //JVM会保证此方法绝对只调用一次
            Singleton(EnumSingleton singleton) {
                this.singleton = singleton;
            }
    
            public EnumSingleton getInstance() {
                return singleton;
            }
        }
    }
    

    JVM会保证枚举类构造方法绝对只调用一次,所以保证了对象实例的唯一性

    饿汉式

    public class Singleton {
        private static final Singleton singleton = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return singleton;
        }
    }
    

    相关文章

      网友评论

        本文标题:线程安全的单例模式

        本文链接:https://www.haomeiwen.com/subject/mnjmsctx.html