一分钟读懂设计模式--单例模式

一、使用场景

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度。

1.需要频繁实例化然后销毁的对象。

2.创建对象时耗时长,耗资源,又经常用到的对象。

3.频繁访问数据库或文件的对象

二、懒汉式

在类加载的时候不被初始化

不加synchronized线程不安全,加synchronized保证线程安全,但是效率低,多数情况下不需要同步。

private static SingletonUtil instance;

//线程不安全
public static SingletonUtil getInstance() {
    if (instance == null) {
            instance = new SingletonUtil();
    }
    return instance;
}
//线程安全
public static synchronized SingletonUtil getInstance2() {
    if (instance == null) {
            instance = new SingletonUtil();
    }
    return instance;
}

三、饿汉式

在类加载时已经完成初始化,线程安全

不管用不用,都已经初始化了,有可能这个类一次都用不到

private static SingletonUtil instance = new SingletonUtil();

public static SingletonUtil getInstance() {
      return instance;
}

饿汉式与懒汉式

懒汉式:在类加载时不被初始化,需要使用synchronized才保证线程安全,加载快,获取对象慢。

饿汉式:在类加载就完成了初始化,不用synchronized,线程安全,加载慢,获取快

四、静态内部类

延迟加载,线程安全

缺点:无法传参

public class SingletonUtil {

    private static class SingletonHolder {
        private static final SingletonUtil instance = new SingletonUtil();
    }

    public static final SingletonUtil getInstance() {
        return SingletonHolder.instance;
    }
}

五、双重校验锁

第一次校验:也就是第一个if,这个时为了代码提高执行效率,由于单例模式只要一次创建实例既可,所以当创建一个实例之后就不用synchronized进入同步代码块了,直接返回就行了。

第二次校验:也就是第二个if,这个校验是防止二次创建实例,例如有一种情况,当singleton还没有创建时,线程T1调用getInstance方法,由于第一次判断signleton==null,此时线程T1准备继续执行,但是资源被线程T2抢占,此时T2也调用了getInstance()方法,同样由于singleton没有实例化,T2同样可以通过第一个if,然后继续往下执行同步代码块,第二个if也通过了,然后T2创建了一个实例singleton。此时T2完成任务,资源回到T1线程,T1也进入同步代码块,如果没有第二个if,那么T1也会创建一个singleton实例,那么就会出现创建多个实例的情况,但是加上第二个if,就可以完全避免这个多线程导致多次创建实例的问题。

public class SingletonUtil {
    private volatile static SingletonUtil singletonUtil;

    public static SingletonUtil getInstance5() {
        if (singletonUtil == null) {
            synchronized (SingletonUtil.class) {
                if (singletonUtil == null) {
                    singletonUtil = new SingletonUtil();
                }
            }
        }
        return singletonUtil;
    }
}

5.1为什么要使用volatile

singletonUtil = new SingletonUtil()不是原子操作,jvm做了三件事:

1.给singletonUtil分配内存

2.调用SingletonUtil的构造函数来初始化,创建对象

3.将singletonUtil对象指向分配的内存空间(执行完这步 singleton才是非 null了)

由于指令重排,可能是1-2-3也可能是1-3-2,如果是1-3-2,那么执行完3之后,2之前,线程2来执行这里,在第一个null判断时,就直接返回,但此时singletonUtil只有内存地址,没有执行2创建对象,那么后续使用就报错。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值