单例模式

一、概述

单例模式是在开发中经常使用到的设计模式,它能保证在JVM中只有一个实例存在。下面来看几种单例模式的写法。

二、饿汉模式

    //饿汉模式
    private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
    
    /**
     * 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
     *
     * @return
     */
    public Object readResolve() {
        return getInstance();
    }

在加载类的时候就创建了一个对象,以空间换时间,并且是线程安全的。

 

三、懒汉模式

    //懒汉模式
    private static Singleton instance;

    private Singleton() {

    }

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

    /**
     * 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
     *
     * @return
     */
    public Object readResolve() {
        return getInstance();
    }

在加载类的时候不会去创建对象,而是在使用时再去创建,以时间换空间,在多线程下是不安全的。

 

四、双重检查

    //双重检查
    private volatile static Singleton instance;

    private Singleton() {

    }

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

    /**
     * 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
     *
     * @return
     */
    public Object readResolve() {
        return getInstance();
    }

在对象被使用时才创建,第一次判空避免非必要加锁,当第一次加载时才会加锁再实例化,这样既能节省空间,又能保证线程安全。但是,JVM存在乱序执行功能,也会出现线程不安全的情况。

什么是乱序执行?看这行代码是怎么在JVM里面执行的,instance = new Singleton();

正常执行分3步:

1、在堆内存开辟内存空间

2、在堆内存实例化Singleton各个参数

3、赋值给instance

由于JVM存在乱序执行,可能还没执行完第2步就先执行了第3步,这样另外一个线程判断instance非空,就直接拿来使用,导致异常。

在jdk 1.6之后,只要在定义时加上volatile关键字就可以解决这个问题,即:private volatile static Singleton instance = null;

volatile确保instance每次都在主存中读取,这样做会使执行效率更慢,但可以保证线程安全。

 

五、静态内部类

    //静态内部类实现单例
    private Singleton() {

    }

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

    public static Singleton getInstance() {

        return SingletonHolder.INSTANCE;
    }

    /**
     * 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
     *
     * @return
     */
    public Object readResolve() {
        return getInstance();
    }

使用静态内部类的好处是:当外部类加载时,不会立即加载内部类,这样instance不会被初始化,减小内存占用。

当调用getInstance时才会被初始化,而且只会new一次,这样既能保证线程安全,同时又延迟了对象的实例化。

所以推荐使用静态内部类来写单例。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值