23中设计模式之单例模式

单例

一、简介

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

实现方式

1、构造函数私有化:作用是在其他代码地方不能new新的该对象的实例。
2、该类的内部提供一个静态方法,当调用该方法时返回该类的引用(如果为空就创建类的实例)。

二、单例的八种实现方式

1、饿汉式

见名知意,一开始就创建类的实例,不管是否能用到。
优点:写法比较简单,就是在类装载的时候就完成实例化。在多线程使用中也能保证一个实例。
缺点:在类装载过程中就完成实例化,如果程序用不到的话,就会造成内存的浪费。

public class Singleton {

    private final static Singleton INSTANCE = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

2、饿汉式(静态代码块)

此种方式是方法1的扩展,当类加载时候执行静态代码块中的代码,完成类的实例化。

public class Singleton {

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

3、懒汉式

见名知意:用到该类时才会去创建该类的实例。
缺点:多线程先会造成多个实例的创建(原因时当执行到singleton == null时其他线程也进入会先创建对象的实例)

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

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

4、懒汉式(synchronized关键字方法)

多线程中可以实现一个实例,但是效率低。

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

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

5、懒汉式(synchronized关键字代码块)

多线程不能实现一个线程的实例化,原因和方法3一样。

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

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

6、双重检查(即方法4和5的结合)

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {}

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

7、静态内部类

当singleton类被加载的时候,此时内部类并不会被加载。当调用getInstance方法时内部类才会被加载,完成实例化。

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

8、枚举

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
枚举类中没有构造方法。所以不可以通过反射拿到该类。

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {

    }
}

三、总结

在多线程情况下可以保证单例的方法:饿汉式及其扩展、synchronize同步方法、synchronized的双重检查、静态内部类和枚举的方式。
推荐使用:双重检查、静态内部类、枚举的方式。实际使用中饿汉式使用较多。

面试题:

为什么双重检查为什么要加volatile?

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {}

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

设计到指令重排,一个线程进来执行到 singleton = new Singleton();这一步时并申请了内存空间,并将类中的变量赋了初始值(此时是半初始化状态),另外一个线程进来此时sigleton并不为空即半初始化状态取类中的变量并不是默认值而是初始值。

在这里插入图片描述

上图即为编译后的二进制文件 :new表示内存已经申请好了 invokespecial是调用构造方法赋默认值 astore_1是将引用赋值给douleCheck,涉及到指令重排4和7的执行顺序先后问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_36655528

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值