【设计模式学习笔记】单例模式

单例设计模式介绍

  单例设计模式,就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个其对象的实例方法。

单例模式八种实现方式

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举

饿汉式(静态常量)

  • 步骤如下
    •  构造器私有化(防止用户new对象)
    • 在类的内部创建对象
    • 向外暴露一个静态的公共方法getInstance
public class Solution {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1); // true
        System.out.println(singleton); // Singleton@1b6d3586
        System.out.println(singleton1); // Singleton@1b6d3586
    }
}
class Singleton {
    // 私有化构造器
    private Singleton() {}
    // 创建对象
    private static final Singleton instance = new Singleton();
    // 静态公共方法
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:在类装载的时候就完成实例化,避免了线程同步的问题。
  • 缺点:在类装载的时候就完成实例化对象,没有懒加载的效果。如果一直没有使用过这个对象,就会造成内存的浪费

饿汉式(静态代码块)

public class Solution {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();
        System.out.println(singleton == singleton1); // true
        System.out.println(singleton); // Singleton@1b6d3586
        System.out.println(singleton1); // Singleton@1b6d3586
    }
}
class Singleton {
    private Singleton() {}
    private static final Singleton instance;
    static {
        instance = new Singleton();
    }
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式(线程不安全)

class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 优点:起到了懒加载的效果,但是只能在单线程下使用
  • 缺点:如果在多线程的环境下,当两个线程同时进入instance == null那么此时就有可能创建两个不同的对象

懒汉式(线程安全,同步方法)

class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 优点:解决线程安全问题
  • 缺点:效率低,多线程情况下,每一个线程在获得实例的时候在调用方法的时候都要进行同步。实际上这个方法只执行一次实例化的方法即可,后面如果想要再次获得对象只需要直接返回即可。但是上面这种方式,在线程没有拿到锁的时候都要进行等待。

懒汉式(线程安全,同步代码块)

class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}
  • 这种方式会引起线程安全问题,在多线程并发的情况下,可能创建两个不同的实例。

双重检查

class Singleton {
    // volatile 保证了可见性和有序性,可以让修改值立即更新到主存
    private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                    // 在堆内存分配对象的内存空间
                    // 在堆内存上初始化对象
                    // 设置 instance 指向刚分配的内存地址
                    // 上面两步可能发生指令重排序,可能造成 instance 无法指向正常的对象
                    // 所以需要加入 volatile 关键字
                }
            }
        }
        return instance;
    }
}
  • 这种方式在多线程开发中经常用到,进行了两次检查,可以解决线程安全问题。
  • 上述代码,实例化代码只执行一次,避免反复的进行方法同步,线程安全,延迟加载,效率较高
  • 在实际开发的过程中,比较推荐这种单例设计模式。

静态内部类

class Singleton {
    // 当 Singleton 进行类加载的时候,静态内部类不会被加载
    private Singleton() {}
    public static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
        // 使用 SingletonInstance 会被装载,只会装载一次
    }
}
  • 这种方式采用类装载机制来保证初始化实例的时候只有一个线程。
  • 静态内部类在Singleton装载的时候,静态内部类不会被加载,在需要调用getInstance方法的时候才会被加载。因为在类加载的时候其中的静态属性只会被加载一次,所以这里JVM可以保证线程安全性,在类进行初始化的时候其他线程是无法进入的。
  • 避免了线程不安全,利用静态内部类的特点实现了延迟加载,效率比较高

枚举

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton1 = Singleton.INSTANCE;
        System.out.println(singleton == singleton1); // true
        System.out.println(singleton.hashCode()); // 460141958
        System.out.println(singleton1.hashCode()); // 460141958
    }
}
// 枚举,使用枚举可以实现单例
enum Singleton {
    INSTANCE;
}
  • 借助JDK 1.5中的枚举来实现单例模式,能够避免线程同步问题,而且能够防止反序列化重新创建新的对象。

单例模式注意事项和细节说明

  • 单例模式保证了系统内存中该类只存在一个对象,节省了系统的资源,对于一些需要频繁创建和销毁的对象,使用单例模式可以提升系统性能。
  • 当想要实例化一个单例类的时候,需要记住相应的获取对象的方法。
  • 使用场景:需要频繁创建和销毁对象、创建对象耗时过多的时候或者耗费资源过多(重量级对象)的时候,但是又经常使用的对象、工具类对象、频繁访问数据库的对象或者文件的对象。例如:数据源、session工厂等。
原文地址:https://www.cnblogs.com/zut-syp/p/14290905.html