理解一下饿汉单例模式、懒汉单例模式(双重检查锁、volatile)

一、单例模式的好处

可以在整个系统中只使用同一个对象,方便更改整个系统的配置信息,也可以节约系统创建多个相同功能对象的开销。
同时,因为单例模式需要只创建一个对象,所以需要把对象的构造器设置为私有(对象都默认有一个无参构造器)

二、饿汉单例模式

理解: 字面意思,这个模式很饿,所需要的对象,在系统一开始运行就创建出来了(直接使用static实例化对象)。代码实现:

public class HungerSingleton {
    //使用static一开始就加载对象
    private static HungerSingleton hungerSingleton = new HungerSingleton();

    //将默认的无参构造器设置为私有
    private HungerSingleton(){
    }

    //外部调用该方法获取饿汉对象
    public HungerSingleton getHungerSingleton(){
        return hungerSingleton;
    }
}

可以看到很简单就实现了饿汉模式,但是无论是否需要该对象,该对象一开始就必须要加载,这无疑增加了系统的开销,这时,我们可以使用懒汉单例模式解决

三、懒汉单例模式

理解: 懒汉,顾名思义,该单例模式很懒,只有在需要的时候才创建需要的对象。先看实现代码,然后解释。

public class LazySingleton {
    //使用volatile防止重排序
    private static volatile LazySingleton lazySingleton;
    //将默认的无参构造器设置为私有
    private LazySingleton(){

    }

    //双重检查锁
    public LazySingleton getLazySingleton() {
        if(lazySingleton == null){
            synchronized (LazySingleton.class){
                if (lazySingleton == null)
                    return lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }
}

疑问:


1. 为什么要使用双重检查锁
版本一:

    
    public LazySingleton getLazySingleton() {
        if(lazySingleton == null){
            return lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }

问题:多线程的情况下,线程的抢占式调度,将有可能导致创建多个对象,单例失效,因此需要加上synchronized同步


版本二:

    
    public synchronized LazySingleton getLazySingleton() {
        if(lazySingleton == null){
            return lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }

问题: 我们需要的是在创建对象时进行同步,但是代码中在创建完对象后,之后的每一次获取使用,都需要经过同步操作,在多线程的情况下,大大降低了效率


版本三(双重检查锁):

    //双重检查锁
    public LazySingleton getLazySingleton() {
        if(lazySingleton == null){
            synchronized (LazySingleton.class){
                if (lazySingleton == null)
                    return lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }

可以看到,我们只是在第一次创建对象的时候进行同步操作,创建完成后的每一次使用,都无需经过同步。

2. 为什么需要使用volatile关键字?
首先看一下对象实例化过程:
1.分配内存空间
2.初始化对象
3.将对象指向刚分配的空间

但是由于编译器等原因的优化,有可能发生重排序顺序可能为:
1.分配内存空间
2.将对象指向刚分配的空间
3.初始化对象

一旦发生了重排序,在多线程的情况下,双重检查锁就会出现问题
在这里插入图片描述
所以需要使用volatile关键字,防止发生重排序操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值