1. 程式人生 > >設計模式之單例模式【內附物件例項化幾種方式、實現執行緒安全幾種方式】

設計模式之單例模式【內附物件例項化幾種方式、實現執行緒安全幾種方式】

繼續來複習常用的設計模式-單例模式,順便回憶一下執行緒安全的幾種實現方式。

一、什麼是單例模式

單例模式,簡單常用的一種設計模式,也很好的體現了程式碼控制物件在記憶體數量的一種方式,主要分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演示完了,如有錯漏,請告知。