这篇文章主要介绍java枚举是怎样保证线程安全的,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
<强>前言强>
写在前面:java SE5提供了一种新的类型java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。本文将深入分析枚举的源码,看一看枚举是怎么实现的,他是如何保证线程安全的,以及为什么用枚举实现的单例是最好的方式。
<强>枚举是如何保证线程安全的强>
要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,枚举就和类一样,只是一个关键字,他并不是一个类,那么枚举是由什么类维护的呢,我们简单的写一个枚举:
public enum t { 春天、夏天、秋天、冬天; }
然后我们使用反编译,看看这段代码到底是怎么实现的,反编译(Java的反编译)后代码内容如下:
public final class T extends 枚举 { 年代,private T (String int 我) { 超级(s, i); } public static T[],值() { 在[]T ; int 我; T at1 []; System.arraycopy (at =,枚举值,美元,0,,at1 =, new T[小姐:=,at.length],, 0,, i); return at1; } public static  T 返回对象的值(String s) { return (T) Enum.valueOf(演示/T, s); } public static  final T 弹簧; public static  final T 夏天; public static  final T 秋天; public static  final T 冬天; private static  final T 枚举值[]美元; 静态 { 时间=SPRING new T (“SPRING",, 0); 时间=SUMMER new T (“SUMMER",, 1); 时间=AUTUMN new T (“AUTUMN",, 2); 时间=WINTER new T (“WINTER",, 3); ENUM VALUES 美元;=,(new T [], { 春天,夏天,秋天,冬天 }); } }
通过反编译后代码我们可以看的到,公众最终类T扩展枚举,说明,该类是继承了Enum类的,同时最后一关键字告诉我们,这个类也是不能被继承的。当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个最终的类型的类继承Enum类,所以枚举类型不能被继承,我们看到这个类中有几个属性和方法。
我们可以看到:
public static final T 弹簧; public static  final T 夏天; public static  final T 秋天; public static  final T 冬天; private static  final T 枚举值[]美元; 静态 { 时间=SPRING new T (“SPRING",, 0); 时间=SUMMER new T (“SUMMER",, 1); 时间=AUTUMN new T (“AUTUMN",, 2); 时间=WINTER new T (“WINTER",, 3); ENUM VALUES 美元;=,(new T [], { 春天,夏天,秋天,冬天 }); }
都是静态类型的,因为静态类型的属性会在类被加载之后被初始化,我们在深度分析Java的类加载器机制(源码级别)和Java类的加载,链接和初始化两个文章中分别介绍过,当一个Java类第一次被真正使用到的时候静态资源被初始化,Java类的加载和初始化过程都是线程安全的,所以,创建一个enum类型是线程安全的。
<强>为什么用枚举实现的单例是最好的方式
强>
在[转+注]单例模式的七种写法中,我们看到一共有七种实现单例的方式,其中,有效的Java作者乔什布洛赫提倡使用枚举的方式,既然大神说这种方式好,那我们就要知道它为什么好吗?
<强> 1。枚举写法简单强>
写法简单这个大家看看[转+注]单例模式的七种写法里面的实现就知道区别了。
public enum EasySingleton { 实例; }
你可以通过EasySingleton.INSTANCE来访问。
<强> 2。枚举自己处理序列化强>
我们知道,以前的所有的单例模式都有一个比较大的问题,就是一旦实现了可序列化的接口之后,就不再是单例得了,因为,每次调用readObject()方法返回的都是一个新创建出来的对象,有一种解决办法就是使用readResolve()方法来避免此事发生。