01-单例模式的8种写法及其优缺点

文章目录

        • 1. 应用场景
        • 2. 8种写法和优缺点
          • 2.1 常量法2种
            • 2.1.1 实现方法
            • 2.1.2 优缺点
          • 2.2 懒汉式
            • 2.2.1 实现方法
            • 2.2.2 优缺点
          • 2.3 解决线程不安全的三种方法
            • 2.3.1 给初始化加锁,效率低
            • 2.3.2 给语句块加锁,达不到目的
            • 2.3.3 双重检查保证线程安全
          • 2.4 静态内部类
          • 2.5 枚举类
        • 3. 总结

1. 应用场景

整个应用过程内存种只有一个对象,比如各种manager,factory等,比如spring的BeanFactory。

2. 8种写法和优缺点

总体原则就是,将类的构造方法的作用域设为private,防止人为通过new实例化,提供一个获取实例的方法,并保证线程安全。

2.1 常量法2种
2.1.1 实现方法

在类中声明一个private常量,并提供一个获取该常量的方法。由jvm保证线程安全,保证内存中只有一个对象,因为每个类在jvm中只加载一次。
方法二:用静态语句块实例化常量,和方法一差不多

/*
* 饿汉式
* 类加载到内存后,就实例化一个对象,由JVM保证线程安全,简单实用
* 缺点:不管这个实例用到与否,类装载时就完成实例化
* 感觉是个伪命题,你不用,装载它干什么
*
* */
public class Singleton01 {
    private  final static Singleton01 singleton = new Singleton01();
    private Singleton01(){}

    public static Singleton01 getInstance() {
        return singleton;
    }

    public static void main(String[] args) {
        Singleton01 singleton1 = Singleton01.getInstance();
        Singleton01 singleton2 = Singleton01.getInstance();
        System.out.println(singleton1 == singleton2);
    }
}
2.1.2 优缺点

优点:简单,不需要自己维护线程安全
缺点:饿汉式,不管使用与否,都会被加载到内存
这是个伪命题,如果不使用,为什么要加载?

2.2 懒汉式
2.2.1 实现方法

获取对象时,判断对象是否实例化,如果未实例化,就初始化对象

/**
 * lazy loading
 * 懒汉式
 * 优点:能够按需要实现初始化
 * 缺点:线程不安全
 * */
public class Singleton02 {
    private static Singleton02 singleton;
    private Singleton02(){}

    public static Singleton02 getInstance() {
        if (singleton == null) {
            singleton = new Singleton02();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton02.getInstance().hashCode());
            }).start();
        }
    }
}
2.2.2 优缺点

优点:能够按需要实现初始化
缺点:线程不安全

2.3 解决线程不安全的三种方法
2.3.1 给初始化加锁,效率低
/**
 * lazy loading
 * 懒汉式
 * 优点:能够按需要实现初始化,线程安全
 * 缺点:执行效率降低
 * */
public class Singleton03 {
    private static Singleton03 singleton;
    private Singleton03(){}

    public static synchronized Singleton03 getInstance() {
        if (singleton == null) {
            singleton = new Singleton03();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton03.getInstance().hashCode());
            }).start();
        }
    }
}

为了提高效率,给语句块加锁

2.3.2 给语句块加锁,达不到目的
/**
 * lazy loading
 * 懒汉式
 * 为了提供效率,给语句块加锁
 * 优点:能够按需要实现初始化
 * 缺点:线程不安全
 * */
public class Singleton04 {
    private static Singleton04 singleton;
    private Singleton04(){}

    public static Singleton04 getInstance() {
        if (singleton == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized(Singleton04.class) {
                singleton = new Singleton04();
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton04.getInstance().hashCode());
            }).start();
        }
    }
}
2.3.3 双重检查保证线程安全

/**
 * lazy loading
 * 懒汉式
 * 为了提供效率,给语句块加锁,双重检查解决线程不安全问题
 * 优点:能够按需要实现初始化
 * 缺点:复杂
 * */
public class Singleton05 {
    private static Singleton05 singleton;
    private Singleton05(){}

    public static Singleton05 getInstance() {
        if (singleton == null) {//该判断不能去掉,否则每次获取对象,都需要加锁,提高效率
            synchronized(Singleton05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (singleton == null) {
                    singleton = new Singleton05();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton05.getInstance().hashCode());
            }).start();
        }
    }
}
2.4 静态内部类
/**
 * 静态内部类
 * jvm保证单例
 * 加载外部类的时候,内部类不会被加载
 * */
public class Singleton06 {
    private Singleton06(){}

    private static class Singleton06Holder {
        private final static Singleton06 singleton = new Singleton06();
    }

    public static Singleton06 getInstance() {
        return Singleton06Holder.singleton;
    }

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton06.getInstance().hashCode());
            }).start();
        }
    }
}
2.5 枚举类
/**
 * java创始人之一,effective Java一书作者书中这样写
 * 枚举类
 * jvm保证线程安全,并且不会被反序列化
 * */
public enum Singleton07 {
    INSTANCE;

    public static void main(String[] args) {
        for (int i =0; i<100; i++) {
            new Thread(() -> {
                System.out.println(Singleton07.INSTANCE.hashCode());
            }).start();
        }
    }
}

3. 总结

兼具按需加载和线程安全两个优点的单例模式是:双重检查加锁,静态内部类和枚举类三种。

你可能感兴趣的:(设计模式,单例模式,java,设计模式)