单例模式

本文探讨了单例模式在Java中的实现,包括饿汉模式和懒汉模式。特别是懒汉模式,分析了其在单线程和多线程环境下的实现,关注线程安全和性能优化,如锁的粒度问题,强调了降低锁的竞争以提高性能的重要性。
摘要由CSDN通过智能技术生成

单例模式

  • 单例模式:程序运行中,类(Person)只会生成一个(Person)对象(实例),大家共用的是同一个实例。
  • 场景:配置项 —— 对象
  • 实现:1.饿汉模式 2.懒汉模式

1.1饿汉模式

// 饿汉模式的单例
public class SingletonHungry {
    // 不允许外边调用构造方法
    private SingletonHungry() {}
    
    // 在类加载的时候就创建对象 只执行一次 没有线程安全问题
    // 原因:类加载时,在JVM内部保证了线程间的同步互斥
    //    (1)只执行一次  (2)线程安全
    private static final SingletonHungry instance = new SingletonHungry();
    public static SingletonHungry getInstance() {
        return instance;
    }
}

1.2懒汉模式

1.2.1懒汉模式-单线程版

/**
 * 懒汉模式的单例:单线程环境下正确
 *              多线程环境下有线程安全问题 
 *               (可见性和原子性的问题)
 */

public class SingletonLazyVersion1 {
    private SingletonLazyVersion1() {}

    // 多线程下instance可见性不能保证
    private static SingletonLazyVersion1 instance = null;
    
    // getInstance 被第一次调用时,意味着有人需要 instance
    // 再进行初始化
    public static SingletonLazyVersion1 getInstance() {
        //多线程情况下原子性不能保证
        // 原子开始
        if (instance == null) {   
            instance = new SingletonLazyVersion1(); // new对象有原子性问题
        }
        // 原子结束
        return instance;
    }
}

多线程环境下有线程安全问题:
1.可见性问题: private static SingletonLazyVersion1 instance = null;
2.原子性问题: if (instance == null) { instance = new SingletonLazyVersion1(); }

1.2.2懒汉模式-多线程版-性能低

// 线程安全版本的懒汉单例
public class SingletonLazyVersion2 {
    private SingletonLazyVersion2() {}
    
    //不用给instance专门加锁 已经保证了instance的可见性了
    //因为getInstance方法释放锁时内存都是可见的(清理工作内存的缓存 读到最新的i) 
    private static SingletonLazyVersion2 instance = null;
    
    //整体用synchronized加锁 保证原子性
    //getInstance()方法是static 对其加锁相当于全局锁(大家用的都是同一把锁) 
    public synchronized static SingletonLazyVersion2 getInstance2() {
        if (instance == null) {
            instance = new SingletonLazyVersion2();
        }
        return instance;
    }
}

缺点:虽保证线程安全,但锁的粒度过大(开始锁住 结束才释放 每次都在竞争锁)。

低性能详细分析:
     在这里插入图片描述

1.2.3懒汉模式-多线程版-二次判断-性能高

public class SingletonLazyVersion3 {
    private SingletonLazyVersion3() {}
   
    //注意:instance必必须加volatile 才能防止synchronized里面代码的重排序带来的问题
    private volatile static SingletonLazyVersion3 instance = null; 
   
    private static SingletonLazyVersion3 getInstance3() {
        if (instance == null) {
            
            //只有在初始化时才需要抢锁 保证只有一个在线程初始化
            synchronized (SingletonLazyVersion3.class) {
                if (instance == null) { //二次判断法 确保instance没有被初始化
                    instance = new SingletonLazyVersion3();
                }
            }            
            /* 直接写 错误 必须二次判断
            //A B C 都为null return
            //假设A抢到锁  A去初始化
            //A初始完后  B、C接着枪锁  B抢到锁B初始化 
            //二次初始化发生错误
            //注意:开始抢锁到抢到锁有时间间隔  有可能期间其他对像已经初始化过了
            synchronized (SingletonLazyVersion3.class) {
                instance = new SingletonLazyVersion3();
            }
            */
        }
        return instance; //不为空 return
    }
}

/*  
未加volatitle
//假设A、B两个线程  
//A进去发现instance = null  加锁  
//二次判断instance = null 
//假设执行初始化时发生重排序:new——赋值——初始化  A被new——赋值,然后A被切出去 
//B进来发现 instance 已经不为null 直接退出去 
//但此时 instance 不可用 发生错错误
//注意:synchronized只能保证外面的程序不会重排序到里面 不能保证里面的代码的顺序
*/

未加volatile存在的问题详细分析:

  在这里插入图片描述
加volatile后线程安全详细分析:
  在这里插入图片描述

1.3锁的粒度问题

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值