Java——单例模式:饿汉、懒汉模式

单例模式是啥?
单例模式就是保证某个类在程序中只存在唯一一份实例对象。
单例模式的实现方式有哪些?
饿汉模式:正如字面,表示很饿,要赶紧吃饭;所以它在类加载的同时,就会创建实例对象。
懒汉模式:表示很懒,能不干就不干;所以它在类加载的同时不会创建实例对象;直到第一次去使用的时候,才会创建实例对象。
饿汉模式的实现

  • 优点:不存在线程安全问题
  • 缺点:资源浪费(当不使用实例时,但他又创建了)
/**
 * DataSource 单列模式
 * 饿汉模式:类加载的同时,创建实例

 */
public class DataSourceSingleton {
    //1.创建一个私有的实例对象
    private  static  DataSourceSingleton dataSourceSingleton=new DataSourceSingleton();
    //2.私有的构造函数(防止外部new实例对象);否则就成多例了
    private  DataSourceSingleton(){

    }
    //3.对外提供这个实例对象
    public static DataSourceSingleton getInstance(){
        return dataSourceSingleton;
    }

}

懒汉模式的实现

  • 优点:不浪费资源
  • 缺点:存在安全问题:当有两个线程同时访问getInstance()方法,都执行到了if(dataSourceSingleton1==null),此时,他们都会去new一个对象,则就不是单列模式了
/**
 * DataSource 单列模式
 * 懒汉模式:第一次使用的时候才去创建实例对象


 */
public class DataSourceSingleton1 {
    //1.创建私有的属性
    private  static  DataSourceSingleton1 dataSourceSingleton1;
    //2.2.私有的构造函数(防止外部类new实例对象);否则就成多例了
    private  DataSourceSingleton1(){

    }
    //3.创建对外提供实例对象的方法
    public  static DataSourceSingleton1 getInstance(){
        if(dataSourceSingleton1==null){
            dataSourceSingleton1=new DataSourceSingleton1();
        }
        return dataSourceSingleton1;
    }


}

为了保证它是单例模式,可以给其上锁synchronized

public  synchronized static DataSourceSingleton1 getInstance(){
        
        if(dataSourceSingleton1==null){
            dataSourceSingleton1=new DataSourceSingleton1();
        }
        return dataSourceSingleton1;
    }
  •  可但这样的话,线程就得一个一个去访问getInstance()方法,效率低
    

懒汉模式的改进

public class DataSourceSingleton2 {
//1.用volatile 修饰 dataSourceSingleton2
    private  static  volatile  DataSourceSingleton2 dataSourceSingleton2;
    private DataSourceSingleton2(){

    }
   //2.使用双重if,降低锁的竞争频率
    public  static  DataSourceSingleton2 getInstance(){
        if(dataSourceSingleton2==null){
            synchronized (dataSourceSingleton2){
                if(dataSourceSingleton2==null){
                    dataSourceSingleton2=new DataSourceSingleton2();
                }
            }
        }
        return dataSourceSingleton2;
    }
}

为啥要使用双重if?

  • 当有多个线程同时调用getInstance()方法时;假设线程1和线程2一起调用getInstance()方法;首先他们都判断到dataSourceSingleton2==null(第1次的if),
  • 然后他们都去竞争锁synchronized (dataSourceSingleton2),假设线程1竞争到了锁,线程1进行(第2次的if)就去创建了实例对象 ,然后释放了锁;
  • 这之后,线程2得到了锁,进行(第2次if)发现实例对象已经被创建过了,就不会再去new对象了,这样就保证了程序的单例模式。

为啥要用用volatile 修饰 dataSourceSingleton2?

volatile除了保证内存可见性,还可以防止指令重排序

首先了解一下dataSourceSingleton2=new DataSourceSingleton2()
,它由3部分组成:

  1. 给对象分配内存
  2. 初始化对象
  3. 设置对象到相应的内存地址上
  • 当程序先添加了线程1,让线程1执行,当线程1判断,上锁,再判断,然后执行到dataSourceSingleton2=new DataSourceSingleton2()时;

  • 但发生了指令重排序:变成 1、3、2;还没有执行2时,程序又添加了线程2,让线程2执行

  • 线程2首次判断dataSourceSingleton2==null时,发现对象已经被创建了,则会返回dataSourceSingleton2对象

  • 但此时虽然线程1创建了这个对象,但并没有初始化这个对象

  • 所以线程2得到的对象并不是完整的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值