彻底玩懂单例模式

本文详细介绍了Java中的单例模式,包括饿汉式和懒汉式的简单实现,以及如何在多线程环境下实现安全的单例模式。通过双重锁检查和枚举类型来确保线程安全。最后讨论了反射对单例模式的影响,并展示了如何利用反射破坏单例,以及如何通过枚举类型创建无法被反射破坏的终极单例模式。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、单例模式是什么?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

二、简单实现(饿汉式的单例和懒汉式的单例)

1.饿汉式

public class esingle {
    private static esingle single =new esingle("张三");
    private String name;
    private esingle(Str
ing name){
            this.name=name;
    }
    public static esingle getInstance(){
        return single;
    }

    public static void main(String[] args) {
        esingle instance = esingle.getInstance();
        esingle instance1 = esingle.getInstance();
        System.out.println(instance);
        System.out.println(instance1);
        System.out.println(instance == instance1);
    }
}

2.懒汉式

public class esingle {
    private static esingle single;
    private String name;
    private esingle(String name){
            this.name=name;
    }
    public static esingle getInstance(){
        if (single==null){
             single=new esingle("张三");
        }
        return single;
    }


    public static void main(String[] args) {

        esingle instance = esingle.getInstance();
        esingle instance1 = esingle.getInstance();
        System.out.println(instance);
        System.out.println(instance1);
        System.out.println(instance == instance1);
    }
}

以上是单例模式的简单实现,但只适用于单一线程(只在单一线程安全),在多线程下是不安全的,原因可以自己百度一下,这里就不详解了。

三、实现在多线程下看上去安全的单例模式(以懒汉式为例)

代码如下(示例):

public class esingle {
    private volatile static esingle single;
    private static boolean zrf=false;
    private String name;
    private esingle(String name){
        synchronized (esingle.class){
            if (zrf==false){
                zrf=true;
                this.name=name;
            }else{
                throw new RuntimeException("实例化失败");
            }
        }

    }
    //双重锁检查
    public static esingle getInstance(){
        if (single==null){
            synchronized (esingle.class){
                if (single==null){
                    single=new esingle("张三");
                }
            }
        }
        return single;
    }
    public static void main(String[] args) throws Exception {
        //用线程池进行测试
        Executor executor=new ThreadPoolExecutor(
               5,10,60, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
       for (int i = 0; i <100; i++) {
           executor.execute(()->{
               esingle instance = esingle.getInstance();
               System.out.println(instance);
            });
        }

    }
}
这样实现单例在多线程下是绝对安全的,但是如果你接触过java的反射知识就可破坏这种单例模式,
你可以在main方法中执行以下代码
 Field zrf = esingle.class.getDeclaredField("zrf");
        zrf.setAccessible(true);
        Constructor<esingle> declaredConstructor = esingle.class.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        esingle lis = declaredConstructor.newInstance("李四");
        zrf.set(lis,false);
        esingle wanwu = declaredConstructor.newInstance("王五");
        System.out.println(wanwu);
        System.out.println(lis);
        System.out.println(wanwu==lis);

执行结果如下

你会惊讶的发现单例被破坏了,那么反射就是无敌的了吗?

请看以下JDK newInstance的源码

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

从中我们可以看出反射是不可以破坏枚举类型的类的(枚举类型天生就是单例的),所有我们可以继续完善我们的代码,

终极单例模式

public enum esingle {
    ESINGLE("张三");
    private  static boolean zrf=false;
    private String name;
    private esingle(String name){
                this.name=name;
    }
    public synchronized static esingle getInstance(){
        return ESINGLE;
    }
}

由于枚举类型的特性我们只能使用饿汉式实现,现在这个单例是目前为主最安全的,不仅是多线程安全,而且无法被反射破坏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值