核心作用:
保證一個類只有一個實例,並且提供一個訪問該實例的全局訪問點
就是整個程序有且僅有一個實例。該類負責創建自己的對象,同時確保只有一個對象被創建。在Java,一般常用在工具類的實現或創建對象需要消耗資源。
常見應用場景:
1、任務管理器
2、回收站
3、網站計數器
4、數據庫連接池
優點:
1、單例模式只能產生一個實例,減少了系統性能的開銷,當一個對象的產生需要比較多的資源時,如“讀取配置,產生其他依賴對象”可以通過在應用啓動時產生一個單例對象,然後永久駐留內存
2、單例模式可以在系統設置一個全局訪問點,優化環境共享資源訪問。
常見的五種單例模式實現方式:
餓漢式(線程安全、調用效率高、不支持延時加載)
懶漢式(線程安全、調用效率低、支持延時加載)
雙重檢查鎖式 (由於JVM底層內部模型原因,偶爾出現問題,不建議使用,但可用voletile修飾實例變量解決問題)
靜態內部類(線程安全、調用效率搞、支持延時加載)
枚舉單例(線程安全、調用效率高、不支持延時加載)
一、餓漢式實現(單例對象立即加載)
package com.hezeu.singleton;
/**
*@ClassnameSingletonDemo01
*@Description 測試餓漢式單例模式
*@Date2020/2/19下午03:48
*@Createdby朱進博 [email protected]
*/
public class SingletonDemo01{
private static SingletonDemo01instance = new SingletonDemo01();
private SingletonDemo01(){}
publicstatic SingletonDemo01 getInstance(){
return instance;
}
}
優點:餓漢式單例模式代碼中,static變量會在類裝載時初始化,此時也不會設計多個線程對象訪問該對象的問題,虛擬機保證只會加再一次類,不會發生併發訪問。 問題:如果只是加載類,不調用getInstance()方法,會造成資源浪費
二、懶漢式實現(單例對象延遲加載)
package com.hezeu.singleton;
/**
*@ClassnameSingletonDemo02
*@Description懶漢式單例模式
*@Date2020/2/19下午03:48
*@Createdby朱進博
*/
public class SingletonDemo02{
private static SingletonDemo02instance;
private SingletonDemo02(){}
public static synchronized SingletonDemo02 getInstance(){
if(instance==null){
instance=newSingletonDemo02();
}
returninstance;
}
}
優點:Lazy load! 延遲加載,懶加載,用時再加載
缺點:資源利用率高了,但是,每次調用getInstance()時都會執行Synchronized同步,併發效率低
三、雙重檢查鎖式實現(不建議使用)
package com.hezeu.singleton;
/**
*@Classname Singleton03
*@Description 雙重檢查鎖機制
*@Date 2020/2/19下午04:03
*@Created by朱進博 [email protected]
*/
public class SingletonDemo03{
private static SingletonDemo03 instance = null;
private SingletonDemo03(){}
public static SingletonDemo03 getInstance(){
if(instance==null){
SingletonDemo03sc=null;
synchronized(SingletonDemo03.class){
if(sc==null){
synchronized(SingletonDemo03.class){
if(sc==null){
sc=newSingletonDemo03();
}
}
instance=sc;
}
}
}
return instance;
}
}
優點:雙重檢查鎖式將同步內容下放到if內部,提高了執行的效率,不必每次獲取對象時都同步,只有第一次才同步。
缺點:由於編譯器優化問題和JVM底層內部模型原型,偶爾出現問題
優化版:
package com.hezeu.singleton;
/**
*@Classname SingletonDemo07
*@Description 優化雙重檢查鎖式
*@Date 2020/2/20下午11:05
*@Created by 朱進博 [email protected]
*/
public class SingletonDemo07{
private static volatile SingletonDemo07 instance;
private SingletonDemo07(){}
public static SingletonDemo07 getInstance(){
if(instance == null){
synchronized(SingletonDemo07.class){
if(instance == null){
instance=newSingletonDemo07();
}
}
}
return instance;
}
}
雙重檢查鎖模式進行了兩次判斷,第一次避免不要的實例,第二次爲了進行同步,並避免了多線程問題,又由於實例化對象創建可能出現JVM重排,在多線程訪問存在風險,因此使用volatile修飾instance實例變量,解決該問題
四、靜態內部類式實現(線程安全、效率高、支持延時加載,也是一種懶加載)
package com.hezeu.singleton;
/**
*@Classname SingletonDemo04
*@Description 靜態內部類實現單例模式
*@Date 2020/2/19 下午04:09
*@Created by 朱進博 [email protected]
*/
public class SingletonDemo04{
private static class SingletonClassInstance{
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance(){
Return SingletonClassInstance.instance;
}
private SingletonDemo04(){}
}
要點:外部類沒有static屬性,不會像餓漢式那樣立即加載對象
優點:只有真正調用getInstance(),纔會加載靜態內部類,加載類時是線程安全的。
Instance是static final類型,保證了內存中只有這樣一個實例存在,而且只能賦值一次,從而保證了線程安全。
五、枚舉實現單例模式
package com.hezeu.singleton;
/**
*@Classname SingletonDemo05
*@Description 枚舉實現單例模式
*@Date 2020/2/19 下午04:14
*@Created by 朱進博 [email protected]
*/
public enum SingletonDemo05{
INTEGER;//枚舉元素,代表一個Singleton實例
//自己的操作
public void singletonOperation(){
//功能處理
}
}
優點:實現簡單
枚舉本身就是單例。JVM底層提供保障,避免通過反射和序列化的漏洞!
缺點:不支持延時加載
反射、反序列化破解單例模式:
package com.hezeu.singleton;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
*@Classname Client02
*@Description 測試反射和反序列化破解單例模式
*@Date 2020/2/19 下午04:20
*@Created by 朱進博 [email protected]
*/
public class Client02{
public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException,IOException{
SingletonDemo06s1=SingletonDemo06.getInstance();
SingletonDemo06s2=SingletonDemo06.getInstance();
Classclazz = Class.forName("com.hezeu.singleton.SingletonDemo06");
Constructor<SingletonDemo06> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
SingletonDemo06s3 = (SingletonDemo06)c.newInstance();
System.out.println(s3==s2);
FileOutputStream fos = new FileOutputStream("f:/1.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.flush();
oos.close();
ObjectInputStreamo is = new ObjectInputStream(new FileInputStream("F:/1.txt"));
SingletonDemo06s4 = (SingletonDemo06)ois.readObject();
System.out.println(s4==s1);
}
}
解決方案:
反射:可以在構造方法中手動拋出異常,判斷如果已經實例化則拋出異常
反序列化:可以通過定義readResolve()防止獲得不同對象。
----反序列化時,如果對象所在類定義了readResolve,定義返回那個對象
單例模式效率測試:
package com.hezeu.singleton;
import java.util.concurrent.CountDownLatch;
/**
*@Classname Client03
*@Description 測試單例模式效率
*@Date 2020/2/19 下午 04:38
*@Createdby 朱進博 [email protected]
*/
public class Client03{
public static void main(String[] args) throws InterruptedException{
int threadNum=10;
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
long start = System.currentTimeMillis();
for(inti=0;i<threadNum;i++){
new Thread(new Runnable(){
@Override
public void run(){
for(inti=0;i<11000000;i++){
//SingletonDemo01instance=SingletonDemo01.getInstance();//餓漢式47
//SingletonDemo02instance=SingletonDemo02.getInstance();//懶漢式3816
//SingletonDemo04instance=SingletonDemo04.getInstance();//靜態內部類18
//SingletonDemo05instance=SingletonDemo05.INSTANCE;//枚舉46
//SingletonDemo07instance=SingletonDemo07.getInstance();//雙重檢查鎖36
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
Long end=System.currentTimeMillis();
System.out.println("時間--->"+(end-start));
}
}
餓漢式 | 47ms |
---|---|
懶漢式 | 3816ms |
靜態內部類 | 18ms |
枚舉式 | 46ms |
靜態內部類 | 18ms |
雙重檢查鎖式 | 36ms |
CountDownLatch:
同步輔助類,在完成一組正在其他線程中執行操作之前,它允許一個或多個線程一直等待 countDown()
當前線程調用此方法,則計數減一 Await() 調用此方法會一直阻塞當前線程,直到計時器值爲0