一文带你了解单例设计模式

1.基本介绍

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

2.实现方式

通常单例模式在Java语言中,有两种构建方式:

  • 懒汉式—线程不安全:最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能。
  • 懒汉式—线程安全:加上synchronize之类保证线程安全的基础上的懒汉模式,相对性能很低,大部分时间并不需要同步
  • 饿汉方式。指全局的单例实例在类装载时构建。 [2]
  • 双检锁式。在懒汉式基础上利用synchronize关键字和volatile关键字确保第一次创建时没有线程间竞争而产生多个实例,仅第一次创建时同步,性能相对较高
  • 登记式。作为创建类的全局属性存在,创建类被装载时创建
  • 枚举。java中枚举类本身也是一种单例模式

K1VK78.png

三大要素:

  • 私有的构造方法;
  • 指向自己实例的私有静态引用;
  • 以自己实例为返回值的静态的公有方法。

3.相关实例

①饿汉式单例(线程安全)

阐述:见名知义,“饿汉”说明它很“饥饿”,迫切需要找到吃的,这里也就是我们所说的实例。为什么说它是线程安全的呢?因为我们一开始就创建一个这样的实例,其他线程在访问前这个实例就已经创建完成,在类的生命周期中只创建一次,所以饿汉单例天生就是线程安全的。

// 饿汉式单例示例
class Singletone {

    private static  Singletone singleton = new Singletone();

    // 懒汉式
    private Singletone() {

    }

    // 保证返回一个实例
    public static Singletone getSingleton() {
        return singleton;
    }

}


public class EHanShi {

    public static void main(String[] args) {
        Singletone singleton1 = Singletone.getSingleton();
        Singletone singleton2 = Singletone.getSingleton();
        System.out.println(singleton1==singleton2);

    }
}
复制代码

输出结果为: true

②懒汉单例(需要加双重检查锁来保证线程安全)

阐述: 所谓懒汉模式,就是像一个“懒汉”一样,需要用到创建实例了程序再去创建实例,不需要创建实例程序就“懒得”去创建实例。为什么说它不是线程安全的呢?因为在高并发的情况下懒汉单例就可能创建多个,这样不仅违背了单例的原则,也会在造成使用不同实例造成的线程安全问题,所以懒汉单例需要加锁。

// 懒汉单例 线程不安全 需要双重检查锁保证在JVM中只能有一个实例
class Singleton {

    // 使用volatile禁止指令重排序
    private static volatile Singleton singleton;

    // 懒汉式
    private Singleton() {

    }

    // 保证返回一个实例
    public static Singleton getSingleton() {
        if (singleton == null) {  // 第一层锁
            synchronized (Singleton.class) {
                if (singleton == null) { //第二层锁
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

public class LanHanShi {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getSingleton();
        Singleton s2 = Singleton.getSingleton();
        System.out.println(s1 == s2);

    }

}
复制代码

输出结果为: true

4.总结

懒汉式和饿汉式区别点:

  • 饿汉模式是典型的空间换取时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候就不需要再判断了,节省了运行时间。但如果一直没有人调用,这种浪费的空间就不值得,特别是在空间不足的情况下。

  • 懒汉模式是这是一种时间换空间的做法,要想线程安全,大家第一想到的便是下面这种方式,就是在getSingleton方法加上synchronized关键字,但是这种方式也有致命的缺点,那就是并发率太低。

猜你喜欢

转载自juejin.im/post/5deba534518825123509356a