Java多线程~以“饿汉”与“懒汉”两种模式分别实现单例模式

目录

什么是单例模式?

如何通过代码实现单例模式?

饿汉模式

懒汉模式

单线程版

多线程版 

双重校验法 


什么是单例模式?

单例模式是设计模式的一种,单例模式即:当某个类提供给别处使用时,只能使用同一个实例化对象。单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例.

如何通过代码实现单例模式?

利用私有的构造方法实现

①饿汉式写法:静态变量初始化时就赋值

②懒汉式写法:初始化时不赋值,等使用的时候如果没有初始化再初始化,这种写法可以说是线程不安全的,也可以说是线程安全的但是效率低

③双重校验锁(最重要的一种写法)

饿汉模式

利用私有的构造方法实现单例模式,在静态变量初始化时就进行赋值

/**
 * Created with IntelliJ IDEA.
 * Description: 单例模式,饿汉式写法
 */
public class HungryManStyle {
    private static HungryManStyle instance = new HungryManStyle();

    private HungryManStyle(){}

    public static HungryManStyle getInstance() {
        return instance;
    }
}

验证单例模式的使用 

    public static void main(String[] args) {
        //模拟使用单例的对象
        HungryManStyle instance1 = HungryManStyle.getInstance();
        HungryManStyle instance2 = HungryManStyle.getInstance();
        System.out.println(instance1 == instance2);
    }

 

懒汉模式

单线程版

类加载的时候不创建实例,第一次使用的时候才创建实例。利用if语句判断instance是否为空,若为空则为第一次使用,为其创建实例对象,否则直接返回对象.

/**
 * Created with IntelliJ IDEA.
 * Description: 单例模式:懒汉式写法单线程
 */
public class LazyMode {
    private static LazyMode instance;
    private LazyMode(){}

    public static LazyMode getInstance() {
        if(instance == null) {
            instance = new LazyMode();
        }
        return instance;
    }
}

多线程版 

上面的单线程版懒汉模式是不安全的。

· 线程的不安全问题发生在首次创建实例对象时,如果多个线程同时调用getInstance方法,就可能创建出多个实例

· 一旦实例已经创建好,后面在多线程环境再调用getInstance方法就不存在线程安全问题了

为了解决这个线程不安全问题,需要对其进行加锁操作,保证每一次只有一个线程可以调用getInstance方法。但是这种写法当实例创建好后,后面在多线程环境下调用该方法时,其效率会十分低下,一次只能有一个线程申请锁成功,得到instance对象,其他线程只能等待该线程运行结束之后才可以竞争锁得到instance对象.

/**
 * Created with IntelliJ IDEA.
 * Description: 单例模式:懒汉式写法多线程
 */
public class LazyMode1 {
    private static LazyMode1 instance;
    private LazyMode1(){}

    public synchronized static LazyMode1 getInstance() {
        if(instance == null) {
            instance = new LazyMode1();
        }
        return instance;
    }
}

双重校验法 

该写法是对于懒汉式多线程版写法的改进,在加锁的基础上进行了进一步的判断,在保证线程安全的同时,也提高了运行效率。

具体操作:①使用双重if判定,降低锁竞争的频率;②给instance加上了volatile

/**
 * Created with IntelliJ IDEA.
 * Description: 单例模式:双重校验锁写法
 */
public class DoubleCheckLock {
    private static volatile DoubleCheckLock instance;
    private DoubleCheckLock(){}
    public static DoubleCheckLock getInstance() {
        if(instance == null) {
            synchronized(DoubleCheckLock.class) {
                if(instance == null) {
                    instance = new DoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

如何理解上述两个操作

加锁/解锁操作是一件开销比较高的操作,而懒汉模式的线程不安全只是发生在首次创建实例时,因此在后续的使用中就不需要再对其进行加锁了,这样可以很大程度上提高效率

外层的if用来判断当前是否已经将instance对象实例化了,同时为了保证“可见性”,对instance加上volatile.

当完成了上述两个操作后,当多线程首次调用getInstance方法时,多个线程都会发现instance为null,然后进行竞争锁,最终只有一个线程成功竞争到锁,然后完成创建实例的操作。当这个实例创建完成之后,其他竞争到锁的线程就被里层的if判断挡住了,也就不会再创建其他实例了.而后续的线程,不必加锁,直接通过外层的if判断语句就知道实例已经创建,从而不进行申请锁操作,直接返回instance对象,降低了开销,提高了效率.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值