雙重校驗(yàn)鎖 實(shí)現(xiàn)單例:
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
枚舉 實(shí)現(xiàn)單例:
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
上面的雙重鎖校驗(yàn)的代碼很臃腫轴猎,是因?yàn)榇蟛糠执a都是在保證線程安全。為了在保證線程安全和鎖粒度之間做權(quán)衡掩幢,代碼難免會(huì)寫的復(fù)雜些或油。但是寞忿,這段代碼還是有問題的,因?yàn)樗麩o(wú)法解決 反序列化會(huì)破壞單例 的問題装哆。
那么罐脊,為什么使用枚舉就不需要解決線程安全問題呢?
其實(shí)在 底層 還是做了線程安全方面的保證的蜕琴。因?yàn)樘摂M機(jī)在加載枚舉的類的時(shí)候萍桌,會(huì)使用
ClassLoader
的loadClass
方法,而這個(gè)方法使用同步代碼塊保證了線程安全凌简,所以上炎,創(chuàng)建一個(gè) enum 類型是線程安全的。
public enum T {
SPRING, SUMMER, AUTUMN, WINTER;
}
反編譯后代碼為:
public final class T extends Enum
{
...
public static final T SPRING;
public static final T SUMMER;
public static final T AUTUMN;
public static final T WINTER;
private static final T ENUM$VALUES[];
static
{
SPRING = new T("SPRING", 0);
SUMMER = new T("SUMMER", 1);
AUTUMN = new T("AUTUMN", 2);
WINTER = new T("WINTER", 3);
ENUM$VALUES = (new T[] {
SPRING, SUMMER, AUTUMN, WINTER
});
}
}
枚舉可解決反序列化會(huì)破壞單例的問題
對(duì)于序列化這件事情雏搂,為什么枚舉又有先天的優(yōu)勢(shì)了呢藕施?
在序列化的時(shí)候 Java 僅僅是將枚舉對(duì)象的 name 屬性輸出到結(jié)果中,反序列化的時(shí)候則是通過 java.lang.Enum 的
valueOf
方法來(lái)根據(jù)名字查找枚舉對(duì)象凸郑。同時(shí)裳食,編譯器是不允許任何對(duì)這種序列化機(jī)制的定制的,因此禁用了writeObject
芙沥、readObject
诲祸、readObjectNoData
、writeReplace
和readResolve
等方法而昨。
枚舉的反序列化并不是通過反射實(shí)現(xiàn)的救氯。所以,也就不會(huì)發(fā)生由于反序列化導(dǎo)致的單例破壞問題歌憨。
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum const " + enumType +"." + name);
}