1.单例模式概述
定义: 保证一个类仅有一个实例,并提供一个全局访问点
类型:创建性
使用场景:想确保任何状态下都绝对只有一个实例
优点:
1. 在内存中只有一个实例, 减少了内存的开销
2.避免资源多重占用
3.设置全局访问点,严格控制访问
2.单例模式的实现方式
1.懒汉式
1)实现方法:
/**
* 单例模式之懒汉式
* */
public class LazySingleton {
//静态私有的要被单利的对象(所谓懒汉式就是它比较懒,初始化的时候不创建)
private static LazySingleton lazySingleton = null ;
//私有的构造器 不让外部程序再次创建新的对象
private LazySingleton(){
}
//公有静态获取实例的方法
public static LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
2)非线程安全
但这种懒汉式实现的单例模式是线程不安全的 ,假设此时有两个线程,线程A走到
lazySingleton = new LazySingleton();
线程B走到
if(lazySingleton == null){
当线程A还没有执行完时, 线程B判断lazySingleton 为null ,这样他就会再次执行new 操作, 那么这个对象就会被new 了两次。
3)解决方法
方案1:
我们在方法上添加synchronized 同步锁
public synchronized static LazySingleton getInstance(){
因为锁添加在静态方法上, 这样锁住的是整个类,每次请求这个方法时,都会判断一下这个锁,这样的效率是不高的
我们可以用双重检查机制,只有当这个对象为空的时候我们再锁住这个类
public synchronized static LazySingleton getInstance(){
if(lazySingleton == null){
//只有当这个对象为空的时候我们才锁住这个类
synchronized (LazySingleton.class){
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
但这样其实也是线程不安全的 ,原因在lazySingleton = new LazySingleton()这行代码。 虽然这只有一行代码,但这行代码总共分为三个步骤
1.分配内存给对象
2. 初始化对象
3. 设置lazySingleton 指向刚才分配的内存地址
但是2和3 有可能发生指令重排序 ,也就是3 走在2 的前面 , 在多线程的环境下, 虽然判断不为空, 但有可能对象并没有被初始化直接将这个对象返回 就会报错, 解决方法,添加 volatile 防止指令重排序
private volatile static LazySingleton lazySingleton = null ;