日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java中枚举的线程安全性及序列化问题

發布時間:2023/12/3 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java中枚举的线程安全性及序列化问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自??Java中枚舉的線程安全性及序列化問題

Java SE5提供了一種新的類型-Java的枚舉類型,關鍵字enum可以將一組具名的值的有限集合創建為一種新的類型,而這些具名的值可以作為常規的程序組件使用,這是一種非常有用的功能。本文將深入分析枚舉的源碼,看一看枚舉是怎么實現的,他是如何保證線程安全的,以及為什么用枚舉實現的單例是最好的方式。

?

枚舉是如何保證線程安全的

要想看源碼,首先得有一個類吧,那么枚舉類型到底是什么類呢?是enum嗎?答案很明顯不是,enum就和class一樣,只是一個關鍵字,他并不是一個類,那么枚舉是由什么類維護的呢,我們簡單的寫一個枚舉:

public?enum?t?{SPRING,SUMMER,AUTUMN,WINTER; }

然后我們使用反編譯,看看這段代碼到底是怎么實現的,反編譯(Java的反編譯)后代碼內容如下:

public?final?class?T?extends?Enum {private?T(String?s,?int?i){super(s,?i);}public?static?T[]?values(){T?at[];int?i;T?at1[];System.arraycopy(at?=?ENUM$VALUES,?0,?at1?=?new?T[i?=?at.length],?0,?i);return?at1;}public?static?T?valueOf(String?s){return?(T)Enum.valueOf(demo/T,?s);}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});} }

通過反編譯后代碼我們可以看到,public final class T extends Enum,說明,該類是繼承了Enum類的,同時final關鍵字告訴我們,這個類也是不能被繼承的。當我們使用enmu來定義一個枚舉類型的時候,編譯器會自動幫我們創建一個final類型的類繼承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});}

都是static類型的,因為static類型的屬性會在類被加載之后被初始化,我們在深度分析Java的ClassLoader機制(源碼級別)和Java類的加載、鏈接和初始化兩個文章中分別介紹過,當一個Java類第一次被真正使用到的時候靜態資源被初始化、Java類的加載和初始化過程都是線程安全的。所以,創建一個enum類型是線程安全的

?

為什么用枚舉實現的單例是最好的方式

在單例模式的七種寫法中,我們看到一共有七種實現單例的方式,其中,Effective Java作者Josh Bloch?提倡使用枚舉的方式,既然大神說這種方式好,那我們就要知道它為什么好?

關于這個問題,我有一篇為什么我墻裂建議大家使用枚舉來實現單例。單獨介紹過,這里再回顧一下。

1. 枚舉寫法簡單

寫法簡單這個大家看看單例模式的七種寫法里面的實現就知道區別了。

public?enum?EasySingleton{INSTANCE; }

你可以通過EasySingleton.INSTANCE來訪問。

2. 枚舉自己處理序列化

我們知道,以前的所有的單例模式都有一個比較大的問題,就是一旦實現了Serializable接口之后,就不再是單例得了,因為,每次調用 readObject()方法返回的都是一個新創建出來的對象,有一種解決辦法就是使用readResolve()方法來避免此事發生。但是,為了保證枚舉類型像Java規范中所說的那樣,每一個枚舉類型極其定義的枚舉變量在JVM中都是唯一的,在枚舉類型的序列化和反序列化上,Java做了特殊的規定。英文原文我就不貼了。

大概意思就是說,在序列化的時候Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是通過java.lang.Enum的valueOf方法來根據名字查找枚舉對象。同時,編譯器是不允許任何對這種序列化機制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 我們看一下這個valueOf方法:

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);??}??

從代碼中可以看到,代碼會嘗試從調用enumType這個Class對象的enumConstantDirectory()方法返回的map中獲取名字為name的枚舉對象,如果不存在就會拋出異常。再進一步跟到enumConstantDirectory()方法,就會發現到最后會以反射的方式調用enumType這個類型的values()靜態方法,也就是上面我們看到的編譯器為我們創建的那個方法,然后用返回結果填充enumType這個Class對象中的enumConstantDirectory屬性。

所以,JVM對序列化有保證。

3.枚舉實例創建是thread-safe(線程安全的)

我們在深度分析Java的ClassLoader機制(源碼級別)和Java類的加載、鏈接和初始化兩個文章中分別介紹過,當一個Java類第一次被真正使用到的時候靜態資源被初始化、Java類的加載和初始化過程都是線程安全的。所以,創建一個enum類型是線程安全的。

總結

以上是生活随笔為你收集整理的Java中枚举的线程安全性及序列化问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。