单例模式

单例模式

本文章根据b站狂神说单例模式视频教程整理
b站狂神说单例模式视频链接
https://www.bilibili.com/video/BV1K54y197iS?from=search&seid=3102450277699477443

饿汉式单例

package com.kuang;

/**
 * @Author: Abe
 * Date: 2020/12/5 14:24
 * 饿汉式单例模式
 */
public class Hungry {

    //造成资源浪费
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];

    //私有化构造器
    private Hungry() {
    }

    private final static Hungry HUNGRY = new Hungry();
    //提供一个方法,供其他对象来调用
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

总结 :饿汉式单例能保证线程安全,并且也只会有一个实例,但是这种方式会在程序一启动的时候把对象创建好,这样就有可能造成资源浪费,影响性能。

懒汉式单例

package com.kuang;

/**
 * @Author: Abe
 * Date: 2020/12/5 14:35
 */
public class LazyMan {
    //构造器私有化
    public LazyMan() {
        System.out.println(Thread.currentThread().getName() + "ok");
    }
    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if(lazyMan == null) {
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }


}

总结:以上这种方式,在多线程的情况下,就会出现问题,会出现多个实例,我们在实际开发中一般都是会出现多线程并发的情况。所以需要改进

改进代码一

//双重检测锁模式 dcl懒汉模式
public static LazyMan getInstance(){
    if(lazyMan == null) {
        synchronized (LazyMan.class){
            if(lazyMan == null) {
                lazyMan = new LazyMan();
            }
        }
    }
    return lazyMan;
}

总结:这种情况下,看起来是没有问题的,并且测试的话,实际情况也是只有实例一个对象,这就符合单例模式,但是在极端情况下还是会有问题的。new LazyMan() 不是原子性操作,实际的操作如下

  1. 分配内存空间
  2. 执行构造方法,初始化对象
  3. 把这个对象指向这个空间

但是在实际中,有可能出现指令重排的现象,实际上是按照顺序执行的,但是也有可能出现执行 1>3>2

这样的话,在上面的代码中就会有问题,在判断对象是否为空的时候,出现不为空,但是还没有完成构造。

办法

private volatile static LazyMan lazyMan;

加上volatile就能解决

静态内部类实现

package com.kuang;

/**
 * @Author: Abe
 * Date: 2020/12/5 14:59
 * 静态内部类
 */
public class Holder {

    //构造器私有化
    public Holder() {

    }

    public Holder getInstace() {
        return InnerClass.HOLDER;
    }

    //静态内部类
    public static class InnerClass {
        private static final  Holder HOLDER = new Holder();
    }

}

枚举单例

package com.kuang;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author: Abe
 * Date: 2020/12/5 15:22
 * 枚举单例
 */
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }

    static class Test{
        public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
            EnumSingle instance = EnumSingle.INSTANCE;
            Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
            declaredConstructor.setAccessible(true);
            EnumSingle instance2 = declaredConstructor.newInstance();

            System.out.println(instance == instance2);
        }
    }

}

使用枚举可以防止使用反射去破坏单例模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值