1. 程式人生 > >設計模式之禪(6)-單件模式

設計模式之禪(6)-單件模式

文章目錄


更多關於設計模式的文章請點選設計模式之禪(0)-目錄頁


單件模式是面向物件設計模式中常用的一種設計模式,它主要的作用是使得某個物件在全域性程式中只有唯一的一個例項,並且在全域性中只有一個建立例項的訪問點。這種模式通常採用線上程池、連線池、單一記錄器等的使用和實現中。

一、單件模式的實現

單件模式Singleton Pattern)在23種設計模式中屬於比較簡單的那一類,隸屬於建立型模式。它的主要思想是在全域性中只有一個獲得例項的建立點,並且將建構函式私有,不能由外部程式隨意地建立它的例項。
在這裡插入圖片描述

  • SingletonObject1
/**
 * @Description: 單件模式構造
 * @CreateDate: Created in 2018/11/29 10:54
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public
class SingletonObject1 { private static SingletonObject1 s; //建構函式私有,外部類無法直接建立該物件 private SingletonObject1(){ } //全域性唯一的獲得例項點 public static SingletonObject1 getInstance(){ //判斷物件是否已經被建立,沒有被建立則新建物件 if( s == null){ s = new SingletonObject1(); } return
s; } }

通過私有化構造器並且提供唯一的例項獲得點,就可以實現SingletonObject在全域性中只存在唯一的一個例項了。

     @Test
        public void fun1() {
            SingletonObject1 singletonObject1 = SingletonObject1.getInstance();
            SingletonObject1 singletonObject2 = SingletonObject1.getInstance();
            System.out.println(singletonObject1+"\n"+singletonObject2);
        }

在這裡插入圖片描述

二、在併發下單件模式的改進

2.1、使用閉鎖測試單件模式的正確性

上面寫的單件模式程式碼似乎已經可以在全域性中只產生一個例項了,可是如果在多執行緒模式下,以上程式碼還能實現單件嗎?我使用了閉鎖來測試併發時是否仍然能獲得唯一的單件類:【關於閉鎖的使用可以閱讀我的一篇文章:Java併發程式設計(9)-使用閉鎖測試併發執行緒安全性

 @Test
    public void concurrentTest(){
        //開始閉鎖
        CountDownLatch startLatch = new CountDownLatch(1);
        for (int i = 0; i < 5; i++) {
            new Thread() {
              public void run(){
                  try {
                      //執行緒執行至閉鎖處等待
                      startLatch.await();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  //5個執行緒併發執行任務
                 new MyTask().run();
                }

            }.start();
        }

        //執行緒全部集結在閉鎖下,開鎖
        startLatch.countDown();
    }

    //任務類
    public class MyTask implements Runnable{

        @Override
        public void run() {
            //獲得單例類
            SingletonObject1 instance = SingletonObject1.getInstance();
            //列印地址
            System.out.println(instance);
        }
    }

在這個測試中,我只讓5個執行緒去併發地獲得單件類例項,可是卻出現了問題,單件的例項並不唯一了:
在這裡插入圖片描述

2.2、使用同步鎖改進單件模式

要使得單件模式在併發條件下仍然正確,最簡單的方式就是使用同步鎖來限制同時讀取例項的執行緒數,這個做法很安全,但是會影響速度:

//全域性唯一的獲得例項點
   public static synchronized SingletonObject1 getInstance(){
       //判斷物件是否已經被建立,沒有被建立則新建物件
       if( s == null){
           s = new SingletonObject1();
       }

       return  s;
   }

2.3、使用二重檢查加鎖來改進單件模式

對於同步鎖而言,還可以使用更加輕量的volatile關鍵字來實現執行緒之間對例項狀態的檢查加鎖:

public class SingletonObject {
   private volatile static SingletonObject instance;

   //構造方法私有化,只能在本類中呼叫構造方法
   private SingletonObject(){

   }
   /**
    *@description 獲得全域性唯一的例項
    *@author arong
    *@date 2018/11/27
    *@param:
    *@return com.iteason.singletonPattern.SingletonObject
    */
   public static SingletonObject getInstance(){
   //雙重檢查加鎖
       if(instance == null){
           synchronized (instance){
               instance = new SingletonObject();
           }
       }
           return instance;
   }
}