1. java單例模式餓漢式會有線程安全問題嗎
1、答案:不會有線程安全問題。
2、首先,一個類可以被使用必須經過 載入、連接和初始化
3、餓漢模式的對象創建是在初始化的時候創建的,初始化操作是jvm執行的(會給類變數賦初始值,執行靜態代碼塊等,類變數是用static修飾的變數),並且在我們看來一個類的初始化只會執行一次(jvm會控制),是不會有線程安全問題出現的
3、糾錯:不是「類一載入就實例化」,而是類先載入、連接(此步驟執行時間不固定,但是在初始化執行前必須執行結束)。初始化執行的時機是你new了一個對象,或者使用了反射機制,或者調用類的靜態變數和方法或者啟動有main方法的類
2. 單例模式的作用及創建方法
單例模式作為常見的設計模式之一,在java的項目開發中會時常的用到。Java Singleton模式即保證在JVM運行時,一個類Class只有一個實例存在。
單例模式有什麼好處呢?
最簡單的一個例子就是網站計數器的設計了。當我們想要統計當前網站的在線人數時,一個顯而易見的問題就是並發所帶來的線程安全問題,當我們對這個計數器(網站人數)在同一時刻進行操作,再保存計數時就會造成數據的混亂,後者覆蓋前者的結果。一種解決方案就是把這個計數器設置為唯一對象,所有人都必須共用同一份數據。
實現唯一對象最好的解決辦法就是讓類自己負責保存它的唯一實例,並且讓這個類保證不會產生第二個實例,同時提供一個讓外部對象訪問該實例的方法。自己的事情自己辦,而不是由別人代辦,這非常符合面向對象的封裝原則。
單例模式的三個特點:
只有在自身需要的時候才會行動,從來不知道及早做好准備。它在需要對象的時候,才判斷是否已有對象,如果沒有就立即創建一個對象,然後返回,如果已有對象就不再創建,立即返回。
該方法在多線程情況下有可能重復創建實例,以下是線程安全的懶漢模式
這種模式的缺點是加鎖造成了效率下降,並且在絕大部分情況下是不需要同步的。使用雙重檢驗鎖(DCL),只在第一次初始化的時候進行同步加鎖
該方式在類載入的時候就被實例化了。
這種方式同樣利用了classloder的機制來保證初始化instance時只有一個線程,它跟餓漢不同的是(很細微的差別):餓漢方式是只要Singleton類被裝載了,那麼instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯式通過調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化instance。
想像一下,如果實例化instance很消耗資源,我想讓它延遲載入,另外一方面,我不希望在Singleton類載入時就實例化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被載入,那麼這個時候實例化instance顯然是不合適的。這個時候,這種方式就顯得很合理。
關於類載入情況下單例模式,如果單例由不同的類裝載器裝入,那便有可能存在多個單例類的實例。假定不是遠端存取,例如一些servlet容器對每個servlet使用完全不同的類 裝載器,這樣的話如果有兩個servlet訪問一個單例類,它們就都會有各自的實例。修復的辦法是
3. 設計模式之單例模式
單例設計模式理解起來非常簡單。一個類只允許創建一個對象(或者實例),那這個類就是一個單例類,這種設計模式就叫單例模式。
下面的示例中如果每個類都創建一個 Logger 實例,就可能造成日誌內容被覆蓋的情況。
如果有些數據在系統中只應保存一份,那就比較適合設計為單例類。比如悄伍,配置信息類,全局 ID 生成器等。
要實現一個單例,我們要考慮以下幾點:
懶漢式相對於餓漢式的優勢是 「支持延遲載入」 。但缺點也很明顯,因為使用了 synchronized 關鍵字導致這個方法的 「並發度很低」 。如果這個單例類偶爾會被用到,那這種實現方式還可以接受。但是,如果頻繁地用到,就會導致性能瓶頸,這種實現方式就不可取了。
這是一種既支持延遲載入、又支持高並發的單例實現方式。
在 java1.5 以下 instance = new Singleton(); 有指令重排問題,需要給 instance 成員變數加上 volatile 關鍵字,java1.5 之後不會再這個問題。
這種方式利用了 Java 的靜態內渣知部類,有點類似餓漢式,但又能做到了延遲載入。
當外部類 Singleton 被載入的時候,並不會創建 SingletonHolder 實例對象。只有當調用 getInstance() 方法時,SingletonHolder 才會被載入,這個時候才會創建 instance。insance 的唯一性、創建過程的線程安全性,都由 JVM 來保證。所以,這種實現方法既保證了線程安全,又能做到延遲載入。
這是一種最簡單的實現方式,基於枚舉類型的單例實現。這種實現方式是通過 Java 枚舉類型本身的特性,保證了實例創建的線程安全性和實例的唯一性。
上面的單例類對象是進程唯一的,一個進程只能有一個單例對象。那如何實現一個線程唯一的單例呢?
假設 IdGenerator 是一個線程唯一的單例類。在線程 A 內,我們可以創建一啟梁或個單例對象 a。因為線程內唯一,在線程 A 內就不能再創建新的 IdGenerator 對象了,而線程間可以不唯一,所以,在另外一個線程 B 內,我們還可以重新創建一個新的單例對象 b。
我們通過一個 ConcurrentHashMap 來存儲對象,其中 key 是線程 ID,value 是對象。這樣我們就可以做到,不同的線程對應不同的對象,同一個線程只能對應一個對象。實際上,Java 語言本身提供了 ThreadLocal 工具類,可以更加輕松地實現線程唯一單例。
4. Java涓23縐嶈捐℃ā寮忊斺斿崟渚嬫ā寮
Java涓栫晫涓鐨勫崟渚嬪湥孌匡細23縐嶈捐℃ā寮忔帰緔
鍦↗ava緙栫▼涓錛屽崟渚嬫ā寮忓傚悓涓搴фˉ姊侊紝榪炴帴鐫瀵硅薄鐨勭敓鍛藉懆鏈熺$悊鍜屽叏灞璁塊棶鐨勭簿濡欏鉤琛°傝╂垜浠涓璧鋒繁鍏ユ帰璁ㄥ叚縐嶅父瑙佷笖鍚勬湁鐗硅壊鐨勫疄鐜版柟寮忥紝瀹冧滑鍒嗗埆鏄錛氭噿奼夊紡銆侀タ奼夊紡銆佸弻閲嶆鏌ラ攣瀹氥侀潤鎬佸唴閮ㄧ被銆佹灇涓炬硶涓嶵hreadLocal錛屼互鍙婁竴縐嶅壋鏂扮殑娉ㄥ唽寮忓崟渚嬫ā寮忋
娉ㄥ唽寮忓崟渚嬬殑闈╂柊錛氶噰鐢ㄩ潤鎬丮ap綆$悊鍗曚緥瀵硅薄錛屽綋闇瑕佹椂錛屽疄渚嬪寲騫惰繑鍥炪傚湪璇稿傜嚎紼嬫睜銆佹暟鎹搴撹繛鎺ユ睜銆佹棩蹇楀硅薄鍜岄厤緗鏂囦歡絳夊満鏅涓錛屽畠鑳戒繚璇侀珮鏁堜笖綰跨▼瀹夊叏鐨勮塊棶銆
鐒惰岋紝鍗曚緥妯″紡騫墮潪涓甯嗛庨『錛屽畠涔熼潰涓存寫鎴橈細綰跨▼瀹夊叏闂棰橀渶瑕佸閥濡欏湴浣跨敤閿佹満鍒訛紝搴忓垪鍖栨椂鍙鑳戒駭鐢熷氫釜瀹炰緥錛岃繖鏃秗eadResolve()鏂規硶灝辨淳涓婄敤鍦轟簡錛涘弽灝勫彲鑳藉艱嚧縐佹湁鏋勯犲嚱鏁拌緇曡繃錛岃繖鏃舵垜浠闇鎶涘嚭寮傚父浠ョず璀﹀憡銆
緇撹錛氬崟渚嬫ā寮忔槸鎺у埗瀵硅薄鐢熷懡鍛ㄦ湡鍜屽疄鐜板叡浜璧勬簮鐨勫叧閿銆傚湪閫夋嫨鍝縐嶅疄鐜版柟寮忔椂錛屽姟蹇呮潈琛$嚎紼嬪畨鍏ㄣ佸簭鍒楀寲鍜屽弽灝勭殑鑰冮噺錛屼互閬垮厤浠g爜澶嶆潅鎬у崌綰у拰鎬ц兘鐨勯殣鎬ф崯鑰椼傚彧鏈夊湪鎮板綋鐨勬椂鏈猴紝瀹冩墠鑳芥垚涓轟唬鐮佽捐′腑鐨勫緱鍔涘姪鎵嬨