设计模式----单例模式的深入了解

深入了解单例模式

一天小C在路上走 突然看见他的好朋友小A和小B 小C正准备和小A,小B打招呼。突然看见小A和小B正在争论什么,小C有些好奇,小C刚上去就听见小A在说单例模式中的线程安全问题。小C心想:“诶,刚好我昨天刚学习了单例模式,这下可以给他们吹一下牛了”。“单例模式中有懒汉式和饿汉式,懒汉式分为线程安全和线程不安全,饿汉式是线程安全的,对吧”,小C说到。小A和小B回头看着小C,这时小A说到:“那你知道懒汉式和饿汉式的安全问题吗?”,“这还不简单吗,懒汉式的可以在方法上添加锁来保证安全,饿汉本来就是线程安全的对吧",小C说到。”不错,但单例模式不仅仅只有这些哦",小A说到。

从线程安全开始

DCL懒汉式

在判断方法中加入双重锁判断

public class Lazy{
    
    private volatile  static Lazy lazy;
    
   	//私有化构造方法
    private Lazy(){}
    
    public static Lazy getLazy(){
        //如果lazy为空 说明是第一次创建
        if(lazy==null){
            //加锁  Lazy.class是唯一的
            synchronized (Lazy.class){
                //在这里判断是避免高并发的时候 多个请求通过了第一个判断  
                //因为上面有一把锁 把别的请求拦在外面  只有一个请求进来
                //当这个请求创建了对象了 那么后面的请求就不需要在创建了 就直接在这里把后面的请求给拦截下来
                if(lazy==null){
                    //创建对象
                    lazy = new Lazy();
                }
            }
        }
        //返回对象
        return lazy;
    }
    
}

因为创建对象不是一个原子性操作:1.分配内存空间,2.创建对象,并且初始化对象,3.对象指向内存空间

这是可能会出现一个指令重排的问题 原本应该执行1,2,3 但可能会出现执行1,3,2 为了避免出现这种情况我们需要给

private static Lazy lazy; 加上volatile关键字 就可以避免指令重排的问题

private volatile static Lazy lazy;

volatile关键字的两层语义

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2)禁止进行指令重排序。

静态内部类

第三种创建单例模式的方法

public class Holder{
    //单例模式的构造方法都是私有化的
    private Holder(){}
    
    public static Holder getInstace(){
        return InnerClass.HOLDER;
    }
    
    public static class InnerClass{
        //final  
        private static final Holder HOLDER = new Holder;
    }
}

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程。

问答(单例模式的深入了解):

小B: 这样也不安全 我可以通过反射来创建

//反射
    public static void main(String[] args) throws Exception {
        Lazy lazy = Lazy.getLazy();

        // 获取构造方法
        // public Constructor[] getConstructors():所有公共构造方法
        // public Constructor[] getDeclaredConstructors():所有构造方法
        //返回Lazy的空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        //修改类中无参构造器的访问权限
        declaredConstructor.setAccessible(true);
        //newInstance创建对象
        //newInstance创建类是这个类必须已经加载过且已经连接
        //newInstance: 弱类型(GC是回收对象的限制条件很低,容易被回收)、低效率、只能调用无参构造
        Lazy lazy1 = declaredConstructor.newInstance();

        System.out.println(lazy);
        System.out.println(lazy1);
    }
//测试结果
demo.Lazy@1540e19d
demo.Lazy@677327b6

小A:“既然你用无参构造来创建 那么我在里面进行判断 就行了 ”

    //私有化构造方法
    private Lazy(){
        //上锁
        synchronized (Lazy.class){
            //判断
            if(lazy!=null){
                throw new RuntimeException("不要想通过反射来破坏结构");
            }
        }
    }

//测试
Caused by: java.lang.RuntimeException: 不要想通过反射来破坏结构
	at demo.Lazy.<init>(Lazy.java:13)
	... 5 more

小B:那我就不创建对象 注解通过反射来创建对象

 //反射
    public static void main(String[] args) throws Exception {
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy lazy1 = declaredConstructor.newInstance();
        Lazy lazy2 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
    }
//测试
demo.Lazy@677327b6
demo.Lazy@14ae5a5

小A:那我就加一个添加一个变量 只要你不知道这个变量名称 那么你也无法创建


    private volatile static Lazy lazy;
    private static  boolean panduan = false;

    //私有化构造方法
    private Lazy(){
        synchronized (Lazy.class){
            if(panduan==false){
                panduan = true;
            }else {
                throw new RuntimeException("不要想通过反射来破坏结构");
            }
        }
    }
    //测试
Caused by: java.lang.RuntimeException: 不要想通过反射来破坏结构
	at demo.Lazy.<init>(Lazy.java:16)
	... 5 more

小B:我可以通过反编译来找到你的变量,再通过修改变量的值继续创建对象


    //反射
    public static void main(String[] args) throws Exception {
        //获取到变量
        Field panduan = Lazy.class.getDeclaredField("panduan");

        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        //修改类中无参构造器的访问权限
        declaredConstructor.setAccessible(true);
        Lazy lazy1 = declaredConstructor.newInstance();
        //修改上一个对象的变量值为false
        panduan.set(lazy1,false);
        Lazy lazy2 = declaredConstructor.newInstance();

        System.out.println(lazy1);
        System.out.println(lazy2);


    }
//测试
demo.Lazy@677327b6
demo.Lazy@14ae5a5

小A :那我就使用枚举类型

public enum  Enum {

    INSTANCE;

    public Enum getInstance(){
        return INSTANCE;
    }

}

小B:我就不信了,我还真就要试试了

public static void main(String[] args) throws Exception {

        Constructor<Enum> declaredConstructor = Enum.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Enum anEnum = declaredConstructor.newInstance();
        Enum anEnum2 = declaredConstructor.newInstance();
        //demo.Enum.<init>()  没有空参的构造器
        System.out.println(anEnum);
        System.out.println(anEnum2);
    }

//测试
Exception in thread "main" java.lang.NoSuchMethodException: demo.Enum.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at demo.test.main(Enum.java:13)

小B:诶? 为什么是没有空参构造器这个异常? 难道不是抛throw new IllegalArgumentException(“Cannot reflectively create enum objects”);异常吗? 我反编译看看 class文件中有没有空参构造方法

E:\collection\HashSet\target\classes\demo>javap -p Enum.class
Compiled from "Enum.java"
public final class demo.Enum extends java.lang.Enum<demo.Enum> {
  public static final demo.Enum INSTANCE;
  private static final demo.Enum[] $VALUES;
  public static demo.Enum[] values();
  public static demo.Enum valueOf(java.lang.String);
  #这里存在
  private demo.Enum();
  static {};

诶? 这class文件中有啊 ,不行我要直接把文件反编译为java文件看看
在这里插入图片描述
通过反编译 发现没有无参构造 有一个有参构造

  public static void main(String[] args) throws Exception {

        Constructor<Enum> declaredConstructor = Enum.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        Enum anEnum = declaredConstructor.newInstance();
        Enum anEnum2 = declaredConstructor.newInstance();
        //demo.Enum.<init>()  没有空参的构造器
        System.out.println(anEnum);
        System.out.println(anEnum2);
    }
//测试
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at demo.test.main(Enum.java:19)

现在这个异常就是 Cannot reflectively create enum objects 不能反射地创建枚举对象

好吧枚举确实不能被反射破坏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值