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

线程安全的单例模式

作者: 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;
    }
}

相关文章

  • 设计模式

    手写单例模式(线程安全) 你知道几种设计模式?单例模式是什么?Spring中怎么实现单例模式?

  • 设计模式(2) 单例模式

    单例模式 线程安全的Singleton 会破坏Singleton的情况 线程级Singleton 单例模式是几个创...

  • 面试复习-设计模式

    一、单例模式 确保一个类只有一个实例,并提供一个全局访问点。 线程不安全的单例模式 懒汉式 线程安全的单例模式: ...

  • Java 常用单例

    一、线程不安全的单例模式 二、线程安全且高效的单例模式 1.双重校验锁 2.静态内部类

  • 设计模式——单例模式

    单例模式 饿汉模式 懒汉模式 线程不安全的模式 线程安全模式 懒汉模式和饿汉模式的区别

  • 单例设计模式笔记

    记录几种单例模式写法。 饿汉模式(线程不安全) 懒汉模式(线程不安全) 懒汉锁模式(线程安全) 懒汉双重判断模式(...

  • 实现单例模式的方式2

    方式一: 能保证线程安全 定义类的时候实现单例模式 方式二: 能保证线程安全 对已定义好的类实现单例模式

  • Java 单例模式

    概述 Java中单例模式是一种常见的设计模式,单例模式总共有7种写法。 懒汉,线程不安全 懒汉,线程安全 饿汉 饿...

  • 设计模式——单例模式的破坏

    概述: 之前学习了单例模式的几种实现,解决了多线程情况下,单例的线程安全问题,保证了单例的实现。但是单例模式在下面...

  • 单例模式与多线程

    这里直接给出几种懒汉模式的单例多线程安全的写法。这种写法就是普通的单例模式,但是是非线程安全的,至于原因,根据前面...

网友评论

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

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