美文网首页
【java】如何用正确用枚举实现线程安全的单例

【java】如何用正确用枚举实现线程安全的单例

作者: 孙小猴猴猴 | 来源:发表于2019-02-28 22:09 被阅读0次

在java中类的加载和初始化过程都是线程安全的,枚举本身也是一个类,在反编译后可以看到枚举的成员使用的是static修饰符修饰,static成员随着类加载进内存之后而创建,所以对enum进行初始化的时候线程也是安全的。
当使用者尝试使用反射的方式创建一个枚举类型对象的时候,jvm会抛出java.lang.IllegalArgumentException: Cannot reflectively create enum objects的异常。所以使用enum创建单例可以最大程度地保证单例的唯一性和安全性。
看到了网上有一些利用枚举来设计单例的做法觉得并不是很好,因为这种方法创建单例,使用者可以通过反射来创建一个新的单例对象,这就破坏了单例的唯一性,代码如下:

public class SharedInstanceDemo {
    
    public static SharedInstanceDemo getInstanceDemo() {
        return singleTonTool.INSTANCE.getInsrtanceDemo();
    }
     // 私有化构造方法
    private SharedInstanceDemo() {}

    private static enum singleTonTool {
        INSTANCE;
        private SharedInstanceDemo instanceDemo;

        private singleTonTool() {
            instanceDemo = new SharedInstanceDemo();
        }

        public SharedInstanceDemo getInsrtanceDemo() {
            return instanceDemo;
        }

    }
}
public class JavaClass {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {

        SharedInstanceDemo o1 = SharedInstanceDemo.getInstanceDemo();
        SharedInstanceDemo o2 = SharedInstanceDemo.getInstanceDemo();
        System.out.println(o1 == o2); // true
        Constructor<?> constructor = SharedInstanceDemo.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        System.out.println(o1 == constructor.newInstance());// false

    }
}

可以看到,如上设计单例,使用者依然可以通过反射创建一个新的单例变量。
正确的创建方式如下:

public enum SharedInstanceDemo{
    INSTANCE; // 这个就是单例的实例对象
    private String nameString;
    public void config(String name) {
        nameString = name;
    }
    public String getNameString() {
        return nameString;
    }
}

这种方式无法通过反射的方式创建一个新的单例变量,保证了单例对象的唯一性,如下:

public class JavaClass {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
        // 枚举对象无法反射创建实例 IllegalArgumentException: Cannot reflectively create enum objects
        Constructor<?> constructor = SharedInstanceDemo.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        SharedInstanceDemo sharedInstanceDemo = (SharedInstanceDemo) constructor.newInstance();
    }
}

单例的使用:

public class JavaClass {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
        // 只能通过INSTANCE获取单例对象
        SharedInstanceDemo demo = SharedInstanceDemo.INSTANCE;
        // 单例对象的方法调用
        SharedInstanceDemo.INSTANCE.config("哈哈哈");
        System.out.println(SharedInstanceDemo.INSTANCE.name());
        // 单例对象的成员变量赋值
        SharedInstanceDemo.INSTANCE.age = 5;
        System.out.println(SharedInstanceDemo.INSTANCE.age);
    }
}

相关文章

  • 【java】如何用正确用枚举实现线程安全的单例

    在java中类的加载和初始化过程都是线程安全的,枚举本身也是一个类,在反编译后可以看到枚举的成员使用的是stati...

  • java单例模式小结

    双检索实现的单例,是线程安全的。 枚举类型实现的单例,目前比较推荐

  • 单例

    单例,是Java中很重要的一个设计模式。 实现单例是要考虑并发(线程安全)问题的。 如何实现一个线程安全的单例?你...

  • 单例模式-Enum枚举单例

    用枚举来实现单例枚举类在多线程情况下也是线程安全的,具体原因下文反编译的时候会提及。 枚举类天然的可序列化机制,能...

  • 单例模式(Singleton)

    一、初始化单例类时即创建单例 饿汉式:(线程安全) 枚举类型:(线程安全) 二、按需、延迟创建单例 懒汉式:(线程...

  • kotlin实现单例

    java实现单例模式 一直习惯于java的写法,java实现单例主要的思想是构造函数私有,然后考虑线程安全,在实现...

  • java单例的几种实现

    以上七种java的单例实现方式。第五种线程安全的单例实现,doublecheck,通过加上volatile,实现真...

  • 单例模式的常用实现方式

    单例模式属于最常用的设计模式,Java中有很多实现单例模式的方式,各有其优缺点 实现方式对比 单例实现方式线程安全...

  • 枚举来实现单例

    双重校验锁 实现单例: 枚举 实现单例: 上面的双重锁校验的代码很臃肿,是因为大部分代码都是在保证线程安全。为了在...

  • 单例模式(Java内部类加载顺序)

    你真的会写单例模式吗——Java实现Android设计模式源码解析之单例模式深度分析 Java 的枚举类型:枚举的...

网友评论

      本文标题:【java】如何用正确用枚举实现线程安全的单例

      本文链接:https://www.haomeiwen.com/subject/bxrluqtx.html