JUC_单例模式

单例模式的核心为构造器私有化,无法通过new创建,需要通过调用类对外提供的方法获取实例

单例模式分为饿汉式与懒汉式,下文分别说明

饿汉式

在类被加载进内存后就直接创建对象实例 可能会造成内存浪费

示例代码

public class HungryMan {
    //构造器私有 无法通过new创建对象实例
    private HungryMan(){
    }

    private final static HungryMan HUNGRY_MAN = new HungryMan();

    public static HungryMan getInstance(){
        return HUNGRY_MAN;
    }
}

DCL懒汉式(Double Check Lock)

只有需要对象实例的时候才创建对象实例 不会造成内存浪费

示例代码

注意使用

public class LazyMan {
    //构造器私有 无法通过new创建对象实例
    private LazyMan(){
        System.out.println(Thread.currentThread().getName() + " 通过 getInstance() 创建了LazyMan对象实例");
    }

    /*
    为什么需要添加volatile以避免指令重排 原因如下:
        new LazyMan()并不是原子性操作 可细分为:
        1.分配内存空间
        2.执行构造方法 初始化对象
        3.将空间分配给该对象
        若发生指令重排 如1->3->2
        若执行完3正在执行2时 恰巧有其他线程调用getInstance()方法 此时其他线程会认为lazyMan!=null(已分配内存空间) 会返回lazyMan
        但此时lazyMan尚未初始化!
     */
    private volatile static LazyMan lazyMan;

    //DCL懒汉式 即double check lock
    public static LazyMan getInstance(){
        //为什么要使用double check?
        //1.若第一层不进行if判断 则每次调用方法都会进行一次锁的争抢 会消耗不必要的资源
        //2.若第二层不进行if判断 则可能同时有多个线程通过第一个if判断进入if代码块(线程均认为lazyMan==null) 会导致线程均等待获取锁 造成重复创建单例
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null) lazyMan = new LazyMan();
            }
        }

        return lazyMan;
    }
}

测试代码

public class Test {
    public static void main(String[] args){
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

测试结果

可以看到,LazyMan类仅被创建了一个实例(构造方法仅执行了一次)

但是!以上的单例都可通过反射破坏,所以可通过枚举类型(enum)来解决该问题

示例代码

public enum Test{
    INSTANCE;

    public void method(){
        System.out.println("method");
    }

    public static Test getInstance(){
        return INSTANCE;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Test.getInstance().method();

        try {
            //获取私有构造方法
            Constructor<Test> constructor = Test.class.getDeclaredConstructor(String.class, int.class);

            //关闭安全验证(无视访问权限进行访问)
            constructor.setAccessible(true);

            //调用构造方法创建实例
            constructor.newInstance();
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

测试结果

可以看到,无法通过反射破坏枚举类型的单例

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值