注意:內容來自網絡
一. 概述
單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一類只有一個實例而且該實例易於外界訪問,從而達到使用目的(如windows操作系統中,任務管理器只能打開一個--主要目的),同時還能方便對實例個數的控制並節約系統資源(主要目的之外的好處)。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
二. 簡介
單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成為系統中的唯一實例。要實現這一點,可以從客戶端對其進行實例化開始。因此需要用一種只允許生成對象類的唯一實例的機制,“阻止”所有想要生成對象的訪問。使用工廠方法來限制實例化過程。這個方法應該是靜態方法(類方法),因為讓類的實例去生成另一個唯一實例毫無意義。
三. 定義
Java中單例模式的定義:一個類有且僅有一個實例,並且自行實例化向整個系統提供。
四. 動機
對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重復對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味着在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的
模式動機。
五. 要點
顯然單例模式的要點有三個:
1. 是某個類只能有一個實例;
2. 是它必須自行創建這個實例;
3. 是它必須自行向整個系統提供這個實例。
從具體實現角度來說,就是以下三點:
1. 是單例模式的類只提供私有的構造函數;
2. 是類定義中含有一個該類的靜態私有對象;
3. 是該類提供了一個靜態的公有的函數用於創建或獲取它本身的靜態私有對象。
六. 實例
當一個類的實例可以有且只有一個的時候就需要用到了。為什么只需要有一個呢?有人說是為了節約內存,但這只是單例模式帶來的一個好處。只有一個實例確實減少內存占用,可是我認為這不是使用
單例模式的理由。我認為使用單例模式的時機是當實例存在多個會引起程序邏輯錯誤的時候。比如類似有序的號碼生成器這樣的東西,怎么可以允許一個應用上存在多個呢?
Singleton模式主要作用是保證在Java應用程序中,一個類Class只有一個實例存在。
1. 懶漢式
所謂“懶漢式”與“餓漢式”的區別,是在於建立單例對象的時間不同。“懶漢式”是在你真正用到的時候才去建這個單例對象:
1 public class SingletonClass{ 2 private static SingletonClass instance=null; 3 public static SingletonClass getInstance() 4 { 5 if(instance==null) 6 { 7 synchronized(SingletonClass.class) 8 { 9 if(instance==null) 10 instance=new SingletonClass(); 11 } 12 } 13 return instance; 14 } 15 private SingletonClass(){ 16 } 17 }
2. 餓漢式:
餓漢式:在不管你用的用不上,一開始就建立這個單例對象。
1 //對第一行static的一些解釋 2 // java允許我們在一個類里面定義靜態類。比如內部類(nested class)。 3 //把nested class封閉起來的類叫外部類。 4 //在java中,我們不能用static修飾頂級類(top level class)。 5 //只有內部類可以為static。 6 public static class Singleton{ 7 //在自己內部定義自己的一個 8 //實例,只供內部調用 9 private static final Singleton instance = new Singleton(); 10 private Singleton(){ 11 //do something 12 } 13 //這里提供了一個供外部訪問本class的靜態方法,可以直接訪問 14 public static Singleton getInstance(){ 15 return instance; 16 } 17 }
3. 雙重鎖的形式
public static class Singleton{ private static Singleton instance=null; private Singleton(){ //do something } public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(null==instance){ instance=new Singleton(); } } } return instance; } }
七. 補充synchronized()的作用?
在多線程的情況下,由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。
由於我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。
如:public synchronized void accessVal(int newVal);synchronized 方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多只有一個處於可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
通常問這個問題應該都是涉及到多線程了,如果是在自己學java多線程編程的話,建議搭好環境多敲代碼試試,會發現很多有意思的事情,無論是自己看書還是百度知道能幫到你的很少。