java-設計模式-單例-有限多例

java單例的實現有多種,這裏介紹簡單的幾種:

單例都是要求 構造方法私有化

1:內部類模式(推薦使用)

特點:能實現懶加載、是線程安全的,不用鎖實現

/**
 * 內部類實現單例
 * 0:只初始化一個
 * 1:懶加載
 * 2:線程安全
 */
public class InnerSingle{
    private InnerSingle(){
        System.out.println("內部類初始化");
    }
    public static InnerSingle getInstance(){
        return InnerHelper.innerSingle;
    }
    private static class InnerHelper{
        private static InnerSingle innerSingle = new InnerSingle();

    }
}

 

2:餓漢模式 - 怕捱餓,先實例

特點:靜態屬性中新建對象

優點:線程安全、性能也高      缺點:不能懶加載

/**
 * 單例 - 餓漢
 */
public class SingleHungry {
    private static SingleHungry singleHungry = new SingleHungry();
    private SingleHungry(){
        System.out.println("初始化");
    }
    public static SingleHungry getInstance(){
        return singleHungry;
    }
}

3:懶漢模式 - 用到再實例

普通模式:不用同步關鍵字,爲線程不安全 

    if(instance == null){

        instance = new Single()
    }

方法鎖模式:在getInstance方法前加入 同步 關鍵字得意實現線程安全。

特點:在初始化完成後,每次調用都需要去競爭鎖,效率特別慢。

有點:解決了線程安全的問題。

public static synchronized getInstance() {
    if(instance == null){
        instance = new Single()
    }
}

雙檢查模式(重點學習)

加入volatile保證變量在不同線程間的 可見性

判斷有無實例(第一次檢查),只有在沒有實例前才需要去走同步代碼

獲取鎖後,還需要再次判斷是否已經實例化了。(第二次檢查,讀者先思考下這裏爲什麼要第二次檢查,後面有限多例模式有解答)

public class SyncSingle {

    /**
     * 對保存實例的變量添加volatile的修飾
     */
    private volatile static SyncSingle instance = null;
    private SyncSingle(){
    }

    public static SyncSingle getInstance(){
//先檢查實例是否存在,如果不存在才進入下面的同步塊
        if(instance == null){
            //同步塊,線程安全的創建實例
            synchronized(SyncSingle.class){
                //再次檢查實例是否存在,如果不存在才真的創建實例
                if(instance == null){
                    instance = new SyncSingle();
                }
            }
        }
        return instance;

    }
}

 

有限多例模式

有限多例的意思是最多能實例化多少個,仿照單例模式的 懶漢的雙檢查 方法 來實現。


/**
 * 有限的多例 3個
 */
public class ThreeSingle {
    private ThreeSingle(){

    }
    private  static volatile   List<ThreeSingle> list = new ArrayList<>();
    private static  final int INSTANCE_MAX = 3;

    public static ThreeSingle getInstance(){
        if(list.size() < INSTANCE_MAX){
            synchronized (ThreeSingle.class){
                System.out.println("list size爲"+list.size());
                if(list.size() < INSTANCE_MAX){//注意這點,需要重新檢查,因爲可能有多個線程(超過3個)等待鎖,但是隻有前3個線程能建立對象
                    ThreeSingle threeSingle = new ThreeSingle();
                    list.add(threeSingle);
                    System.out.println("add");
                }
            }
        }
//        System.out.println(list.size());
        return list.get(new Random().nextInt(list.size()));
    }

}

測試方法

 public static void main(String[] args) {
        for (int i =0;i<100;i++){
            new Thread(()->{
                ThreeSingle.getInstance();
            }).start();
        }
    }

得到的結果爲:

list size爲0
add
list size爲1
add
list size爲2
add
list size爲3
list size爲3
list size爲3
list size爲3
list size爲3

 

結果中有多個 list size爲3 ,說明在有超過3個線程在等待鎖,獲取鎖之後,可能 list 已經超過3個了,所以這裏需要再次檢查。這就是爲什麼需要雙檢查的原因

如果你的多線程的知識儲備不夠,建議可以看看筆者的另外一篇博文:https://blog.csdn.net/xiaoluo5238/article/details/104380207

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章