# 单例模式

# 饿汉式(线程安全)

public class Singleton01 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton1 == singleton2);//true
        System.out.println(singleton1.hashCode() == singleton2.hashCode());//true
    }
}

class Singleton {
    // 构造私有化对象,防止外部new
    private Singleton() {
    }

    // 本类内部创建对象实例
    private static final Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

}
  • 优点

    写法简单,类加载的时候就会实例化,避免了线程同步问题

  • 缺点

    类加载的时候就实例化,没有达到lazy loading效果

# 懒汉式(线程不安全)

public class Singleton02Test {
    public static void main(String[] args) {
        Singleton021 singleton1 = Singleton021.getInstance();
        Singleton021 singleton2 = Singleton021.getInstance();
        System.out.println(singleton1 == singleton2);
        System.out.println(singleton1.hashCode() == singleton2.hashCode());
    }
}

// 多线程不安全
class Singleton021 {
    // 构造私有化对象,防止外部new
    private Singleton021() {
    }

    // 本类内部创建对象实例
    private static Singleton021 instance;

    // 使用的时候才去创建
    public static Singleton021 getInstance() {
        if (instance == null){
            instance = new Singleton021();
        }
        return instance;
    }
}

# 双重校验锁

public class Singleton03Test {
    public static void main(String[] args) {
        Singleton03 singleton3 = Singleton03.getInstance();
        Singleton03 singleton4 = Singleton03.getInstance();
        System.out.println(singleton3 == singleton4);
        System.out.println(singleton3.hashCode() == singleton4.hashCode());
    }
}

// 线程安全,效率太低
class Singleton03 {
    // 构造私有化对象,防止外部new
    private Singleton03() {
    }

    // 本类内部创建对象实例
    private static volatile Singleton03 instance;

    public static Singleton03 getInstance() {
        if (instance == null) {
            synchronized (Singleton03.class){
                if (instance == null){
                    instance = new Singleton03();
                }
            }
        }
        return instance;
    }

}

instance = new Singleton03() 这句话分为三步执行:

  1. 为 singleton 分配内存空间
  2. 初始化 singleton
  3. singleton 指向分配的内存空间。

Java 语言规规定了线程执行程序时需要遵守 intra-thread semantics。**intra-thread semantics ** 保证重排序不会改变单线程内的程序执行结果。这个重排序在没有改变单线程程序的执行结果的前提下,可以提高程序的执行性能。

虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就带来一些问题。

由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 getInstance() 后发现 singleton 不为空,因此返回 singleton, 但是此时的 singleton 还没有被初始化。

使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。

# 静态内部类

public class Singleton04Test {
    public static void main(String[] args) {
        Singleton01 singleton1 = Singleton01.getInstance();
        Singleton01 singleton2 = Singleton01.getInstance();
        System.out.println(singleton1 == singleton2);
        System.out.println(singleton1.hashCode() == singleton2.hashCode());
    }
}

class Singleton04 {
    // 构造私有化对象,防止外部new
    private Singleton04() {
    }

    /**
     * 静态内部类在外部类装载的时候,不会加载
     * 调用getInstance的时候会去加载
     * JVM加载类的时候是线程安全的
     * */

    private static class Singleton04Instance {
        private static final Singleton04 singleton04 = new Singleton04();
    }

    public static Singleton04 getInstance() {
        return Singleton04Instance.singleton04;
    }
}

类的静态属性只有在第一次加载的时候才会初始化

# 枚举

public class Singleton05Test {
    public static void main(String[] args) {
        Singleton05 singleton1 = Singleton05.INSTANCE;
        Singleton05 singleton2 = Singleton05.INSTANCE;
        System.out.println(singleton1 == singleton2);
        System.out.println(singleton1.hashCode() == singleton2.hashCode());
        System.out.println(singleton1.say());
    }
}

enum Singleton05 {
    INSTANCE;
    public String say(){
        return "Are You OK?";
    }
}
上次更新: 2022/05/20 14:13:37