Advertisement

枚举类型为什么是线程安全的?为什么反序列化枚举类型也不会创建新的实例?

阅读量:

枚举类型在Java中由于其特殊的初始化和反序列化机制,在使用单例模式时具有天然的线程安全性。具体来说,在第一次被真正用到时,虚拟机会加载并初始化该枚举类型实例,并且这种初始化过程是线程安全的。此外,在反序列化过程中,Java通过Enum.valueOf方法根据名称查找枚举对象而非通过反射调用构造函数来重新创建实例。由于编译器禁止对默认反序列化机制的定制(禁用了writeObject、readObject等方法),因此不会导致新实例被创建或破坏单例状态。这些特性使得使用双重校验锁实现的单例在处理序列化操作时更加稳定可靠。

枚举类型为什么是线程安全的?

我们提出的一个枚举,在首次实际使用时会被虚拟机加载并执行初始化操作,并且这一过程是thread-safe的。我们都知道,为了解决单例并发问题的核心任务就是确保初始化过程中实现thread-safe。

所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。

为什么反序列化枚举类型也不会创建新的实例?

在进行序列化的操作时,Java对于枚举类型的处理较为简单:它仅将枚举对象的名称信息进行编码存储;而在解码过程中,则通过调用系统提供的Enum类中的valueOf方法来恢复原始对象类型。值得注意的是,在编译器层面对此特殊的编码机制进行了严格限制:无法自定义此枚举类型的编码方式,并因此禁止了包括writeObject、readObject、readObjectNoData以及writeReplace等常用的方法被用于此类别对象的操作;此外,在解码过程中还特意排除了readResolve这一操作选项以确保数据的安全性与稳定性

在Java类的反序列化过程中,在不使用反射的情况下会默认调用类的默认构造函数来初始化对象。然而,在某些特殊情况下(例如当单例中使用的构造函数被声明为私有时),即使调用者无法直接访问这些构造函数代码(只能通过反射的方式进行操作),对象依然会被自动初始化并赋值实例变量并执行必要的初始化操作)。这表明通过反序列化得到的对象并不满足单例模式的要求(即对象必须由系统自动管理其生命周期)。需要注意的是,在使用双重校验锁实现的单例模式中存在潜在问题:这种设计可能会导致当实例被反序列化后立即被释放而无法保持持久性(例如当实例被其他线程访问并进行解码时)。因此这种基于反射实现的单例模式并不是一个可靠的选择

源码基于jdk1.8

源码基于jdk1.8

源码基于jdk1.8

参考:https://www.hollischuang.com/archives/2498

Serialization of Enum Constants

全部评论 (0)

还没有任何评论哟~