(1)保證一個類只能有一個實例(一個對象)spring
(2)而且提供一個供外界訪問該實例的全局訪問點數據庫
(1)windows的任務管理器、回收站編程
(2)項目中,讀取配置文件的類,通常只有一個對象。不必每次使用配置文件的數據都要new一個對象去讀取windows
(3)網站的計數器,通常採用單例模式設計,不然沒法作到同步設計模式
(4)數據庫的鏈接池設計,通常使用單例模式設計安全
(5)在spring中,每一個bean默認就是單例模式,這樣作的優勢是spring容器方便管理多線程
(6)在servlet編程中,每一個Servlet也是單例模式併發
(1)減小系統性能的開銷:當一個對象的產生須要比較多的資源時,如須要讀取配置、產生其餘依賴對象時,則能夠經過在應用啓動時直接產生一個單例對象,而後永久存留在內存中的方式來解決。ide
(2)能夠在系統設置全局的訪問點。優化共享資源訪問,例如能夠設計一個單例類,負責全部數據表的映射處理性能
特色:線程安全、調用效率高;可是,不能延時加載
在系統啓動的時候,加載該類的時候,因爲static的緣由,會當即去加載該類的實例化,同時也因爲是static,該類在內存中只有這一個,達到單例的要求。可是,因爲是當即加載,會佔用系統存儲空間,有必定的缺陷。
因爲將無參構造器私有化,全部外界想要使用該類,必須經過提供的全局惟一訪問點,拿到該類的實例化對象。無論外界獲取了多少次對象,在內存中,該類的實例對象只有一個。
public class SingletonDemo02 { private static /*final*/ SingletonDemo02 s = new SingletonDemo02(); private SingletonDemo02(){} // 私有化構造器 public static /*synchronized*/ SingletonDemo02 getInstance(){ return s; } }
特色:線程安全、調用效率不高;可是,能夠延時加載
在系統啓動的時候,加載該類時,並不會當即去初始化該類;
而是在調用該類的getInstance()方法時,判斷當前類是否被建立,若是沒有被建立,則進行建立對象,返回。若是已經建立了,直接返回對象。
同時,考慮到併發的狀況下,須要使用synchronized,保證每次只有一個線程去訪問該類的方法。保證明例化對象的惟一性。
這種方式,屬於延遲加載,真正用到該類的時候纔會去加載。提升了資源利用率,可是調用getInstance()方法都要同步,併發效率低下。
public class SingletonDemo01 { private static SingletonDemo01 s; private SingletonDemo01(){} // 私有化構造器 public static synchronized SingletonDemo01 getInstance(){ if(s==null){ s = new SingletonDemo01(); } return s; } }
特色:因爲JVM底層內部模型緣由,偶爾會出問題,不建議使用
這個模式將同步內容下方到if內部,提升了執行的效率沒必要每次獲取對象時都進行同步,只有第一次才同步建立了之後就不必了。
public class SingletonDemo03 { private static SingletonDemo03 instance = null; public static SingletonDemo03 getInstance() { if (instance == null) { SingletonDemo03 sc; synchronized (SingletonDemo03.class) { sc = instance; if (sc == null) { synchronized (SingletonDemo03.class) { if(sc == null) { sc = new SingletonDemo03(); } } instance = sc; } } } return instance; } private SingletonDemo03() { } }
特色:線程安全、調用效率高;可是,能夠延時加載
將實例化操做放到靜態內部類中,外部類沒有static,因此,在初始化類的時候,不會當即去加載靜態內部類,固然也不會去實例化對象,達到了延遲加載的特性。
只有在調用了getInstance()方法的時候,纔會去加載靜態內部類。加載類的時候,線程是安全的。
instance是static final修飾的,保證了全局惟一性,即單例性。
兼備併發高效率調用和延遲加載特性。
public class SingletonDemo04 { private static class SingletonClassInstance { private static final SingletonDemo04 instance = new SingletonDemo04(); } public static SingletonDemo04 getInstance() { return SingletonClassInstance.instance; } private SingletonDemo04() { } }
特色:線程安全、調用效率高,不能延時加載
枚舉自己就是單例模式。由JVM從根本上提供保障!避免經過反射和反序列化的漏洞!
public enum SingletonDemo05 { /** * 定義一個枚舉的元素,它就表明了Singleton 的一個實例。 */ INSTANCE; /** * 單例能夠有本身的操做 */ public void singletonOperation(){ // 功能處理 } }
//經過反射的方式直接調用私有構造器 Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.singleton.SingletonDemo6"); Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); SingletonDemo6 s3 = c.newInstance(); SingletonDemo6 s4 = c.newInstance(); System.out.println(s3); System.out.println(s4);
如何避免反射破解單例?
經過在構造方法中手動拋出異常
private SingletonDemo6(){ //私有化構造器 if(instance!=null){ throw new RuntimeException(); } }
//經過反序列化的方式構造多個對象 FileOutputStream fos = new FileOutputStream("d:/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); SingletonDemo6 s3 = (SingletonDemo6) ois.readObject(); System.out.println(s3);
如何避免反序列化破解單例模式?
表示:在反序列化的時候,若是已經定義了readResolver()方法,則直接返回此方法指定的對象,而不須要單獨再建立新對象。
private Object readResolve() throws ObjectStreamException { return instance; }
CountDownLatch類:
同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一個或者多個線程一直等待。
countDown():當前線程調用此方法,則計數減一,減一放在finally中執行
await():調用此方法會一直阻塞當前線程,直到計數器爲0;
public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); int threadNum = 10; final CountDownLatch countDownLatch = new CountDownLatch(threadNum); for(int i=0;i<threadNum;i++){ new Thread(new Runnable() { @Override public void run() { for(int i=0;i<1000000;i++){ // Object o = SingletonDemo4.getInstance(); Object o = SingletonDemo5.INSTANCE; } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); //main線程阻塞,直到計數器變爲0,纔會繼續往下執行! long end = System.currentTimeMillis(); System.out.println("總耗時:"+(end-start)); }
(1)單例對象,佔用資源少,不須要延遲加載
枚舉式優於餓漢式
(2)單例對象,佔用資源大,須要延時加載
靜態內部類式優於懶漢式