設計模式之單例模式【內附物件例項化幾種方式、實現執行緒安全幾種方式】
繼續來複習常用的設計模式-單例模式,順便回憶一下執行緒安全的幾種實現方式。
一、什麼是單例模式
單例模式,簡單常用的一種設計模式,也很好的體現了程式碼控制物件在記憶體數量的一種方式,主要分2種實現方式:
①餓漢式,執行緒安全
②懶漢式,執行緒不安全(新增鎖機制,可以實現執行緒安全)
個人理解:顧名思義,就是單例,單個例項,在記憶體當中保持只有一個例項的方式。
解決問題:控制了類的例項化數量,減少了不必要的記憶體消耗。
二、demo演示
⑴餓漢式(執行緒安全):
/** * @param * @Title: Dog * @Description:單例模式(不提供例項化構造方法,設為私有,提供獲取唯一例項的方法) * 餓漢模式,類載入時,就例項化物件,造成記憶體浪費,執行緒安全的,使用靜態常量變數,保證例項唯一。 * @return * @throws */ public class Hungry_dog { private static final Hungry_dog dog=new Hungry_dog(); private Hungry_dog(){ } public static Hungry_dog getDog(){ return dog; } public void call(){ System.out.println("單例模式餓漢式,執行緒安全"); } }
1.為了首次載入類時,就例項化物件,則必須將例項放在靜態程式碼區(因為只加載一次,所以併發訪問時,不存線上程安全問題)。
2.為了實現類的物件例項在記憶體當中唯一,不提供使用new方式進行例項化,故把建構函式設為私有化。
3.但得必須提供一個外部可以獲取該例項的方法,所以必須實現一個對外開放的獲取例項的方法。
⑵懶漢式[延遲/按需載入](執行緒不安全)
/** * @param * @Title: Dog * @Description:單例模式(不提供例項化構造方法,設為私有,提供獲取唯一例項的方法) * 懶漢模式,類載入時,不例項化物件,等被呼叫獲取物件方法時再例項化物件 * 執行緒不安全 * @return * @throws */ public class Lazy_dog{ private static Lazy_dog lazy_dog; private Lazy_dog(){} public static Lazy_dog getLazy_dog(){ if(lazy_dog==null){ try { Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog"); Object object=c.newInstance(); Thread.sleep(3000);//測試執行緒是否安全 lazy_dog=(Lazy_dog)object; }catch (Exception e){ System.out.println("反射建立例項Lazy_dog失敗"); } } return lazy_dog; } public void call(){ System.out.println("Lazy_dog-反射建立例項成功!"); } }
測試一下執行緒是否安全:
public class Test { public static void main(String[] args) { //測試執行緒是否安全 int a=0; while (a<10){ new Thread(new Runnable() { @Override public void run() { Lazy_dog lazy_dog= Lazy_dog.getLazy_dog(); System.out.println(lazy_dog); } }).start(); a++; } } }
輸出:可以看到在多執行緒的情況下產生了很多例項,證明確實是執行緒不安全
⑶懶漢式[延遲/按需載入](執行緒安全),最簡單的處理方式,在建立例項的方法上新增同步鎖
優點:執行緒安全
缺點:效能下降,並且把整個類的物件新增的執行緒鎖
/**
* @param
* @Title: Dog
* @Description:單例模式(不提供例項化構造方法,設為私有,提供獲取唯一例項的方法)
* 懶漢模式,類載入時,不例項化物件,等被呼叫獲取物件方法時再例項化物件
* 新增方法同步鎖-執行緒安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private Lazy_dog(){}
public synchronized static Lazy_dog getLazy_dog(){
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射建立例項Lazy_dog失敗");
}
}
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射建立例項成功!");
}
}
輸出:可以看到在多執行緒的情況下產生同一例項
⑷懶漢式[延遲/按需載入](執行緒安全),在建立例項的方法內特定的程式碼新增同步鎖
優點:執行緒安全,效能較好
缺點:對效能還是較大影響
/**
* @param
* @Title: Dog
* @Description:單例模式(不提供例項化構造方法,設為私有,提供獲取唯一例項的方法)
* 懶漢模式,類載入時,不例項化物件,等被呼叫獲取物件方法時再例項化物件
* 新增同步鎖-執行緒安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private Lazy_dog(){}
public static Lazy_dog getLazy_dog(){
String temp="test";
synchronized(temp){
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射建立例項Lazy_dog失敗");
}
}
}
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射建立例項成功!");
}
}
輸出:可以看到在多執行緒的情況下產生同一例項
⑸懶漢式[延遲/按需載入](執行緒安全),在建立例項的方法內特定的程式碼新增重入鎖
優點:執行緒安全,效能較好,重入鎖更靈活
缺點:對效能有影響
/**
* @param
* @Title: Dog
* @Description:單例模式(不提供例項化構造方法,設為私有,提供獲取唯一例項的方法)
* 懶漢模式,類載入時,不例項化物件,等被呼叫獲取物件方法時再例項化物件
* 新增同步鎖-執行緒安全
* @return
* @throws
*/
public class Lazy_dog{
private static Lazy_dog lazy_dog;
private static Lock lock=new ReentrantLock();
private Lazy_dog(){}
public static Lazy_dog getLazy_dog(){
lock.lock();
if(lazy_dog==null){
try {
Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
Object object=c.newInstance();
Thread.sleep(3000);
lazy_dog=(Lazy_dog)object;
}catch (Exception e){
System.out.println("反射建立例項Lazy_dog失敗");
}
}
lock.unlock();
return lazy_dog;
}
public void call(){
System.out.println("Lazy_dog-反射建立例項成功!");
}
}
輸出:可以看到在多執行緒的情況下產生同一例項
以上2-5都是懶漢式實現的單例模式,各有優缺點,主要演示執行緒安全的幾種方式。
1.第2個例子為了首次載入類時,不例項化物件,在首次呼叫時,才例項化物件(因為首次呼叫時,有可能發生多執行緒同時呼叫,所以併發訪問時,存線上程安全問題)。
2.第3-5個例子為了首次載入類時,不例項化物件,在首次呼叫時,才例項化物件(因為首次呼叫時,有可能發生多執行緒同時呼叫,因此使用執行緒鎖的方式進行限制,所以併發訪問時,也不存線上程安全問題)。
3.為了實現類的物件例項在記憶體當中唯一,不提供使用new方式進行例項化,故把建構函式設為私有化。
4.但得必須提供一個外部可以獲取該例項的方法,所以必須實現一個對外開放的獲取例項的方法。
至此,單例模式的demo演示完了,如有錯漏,請告知。