1. 程式人生 > >【Java多執行緒】單例模式與多執行緒

【Java多執行緒】單例模式與多執行緒

單例模式大家都不陌生,即讓一個類只有一個例項。
單例模式分為懶漢式和餓漢式。
懶漢式☞方法呼叫時再例項化物件,什麼時候用什麼時候例項化,比較懶。
餓漢式☞方法呼叫前物件就已經建立好了,比較有捉急。
本文著重描述懶漢式與多執行緒的內容。

1.餓漢式

public class SingletonHungary {

    private static SingletonHungary instance = new SingletonHungary();

    private SingletonHungary(){}

    public static SingletonHungary getInstance() {
        return instance;
    }
}

public class Main extends Thread{
    @Override
    public void run() {
        System.out.println(SingletonHungary.getInstance().hashCode());
    }

    public static void main(String[] args) {

        Main[] mts = new Main[10];
        for(int i = 0 ; i < mts.length ; i++){
            mts[i] = new Main();
        }

        for (int j = 0; j < mts.length; j++) {
            mts[j].start();
        }
    }
}

由打印出的hashcode相同說明:他們是同一個物件
這裡寫圖片描述

餓漢式在方法呼叫之前就建立了靜態物件,靜態物件是全域性唯一的,即使有多個執行緒訪問也是在使用同一個物件。這樣的方式是執行緒不安全的。

2.懶漢式
(懶漢式的實現方式有多種,最終演化出了一種最優的方式,此處只介紹這種最優方式)

public class DubbleSingleton {

    private static volatile  DubbleSingleton ds;
    public static DubbleSingleton getDs(){
        if (ds==null){
            System.out.println(Thread.currentThread().getName()+":"+"第一輪判斷,我的ds==null,我要去搶建立物件的鎖啦!");
            try {
                //模擬初始化物件的準備時間...
                System.out.println(Thread.currentThread().getName()+":"+"模擬初始化物件的準備時間...");
                Thread.sleep(3000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            synchronized (DubbleSingleton.class){
                System.out.println(Thread.currentThread().getName()+":"+"第二輪判斷,我的ds==null,我來建立物件啦!");
                if(ds==null){
                    System.out.println(Thread.currentThread().getName()+":"+"哈哈哈哈哈!是我建立的物件!");
                    ds = new DubbleSingleton();
                }else {
                    System.out.println(Thread.currentThread().getName()+":"+"第二輪判斷之後,我的ds!=null,看來有人在我之前把物件建立了!");
                }

            }
        }
        return ds;
    }

    public static void main(String[] args){
          Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t2");
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t3");
        t1.start();
        t2.start();
        t3.start();
    }

}

請讀者先思考一個問題再繼續往下看:
為什麼要判斷兩次ds==null?

-------------------------------------------------------------思考分割線----------------------------------------------------------------

我們來看一下列印結果:
這裡寫圖片描述
因為懶漢式是方法呼叫時再建立物件,所以在多執行緒高併發時為了保證安全,並且高效,在程式碼塊上加了synchronized關鍵字,多個執行緒可以同時呼叫getDS方法,這時他們的ds都是null,但當一個執行緒在建立一個物件後,由於其他執行緒已經在第一輪判斷時就進入了方法,所以其他執行緒會繼續走synchronized程式碼塊,這時進行第二輪判斷,其他執行緒會在第二輪判斷時發現物件已經建立,則不會再繼續建立物件。
這裡寫圖片描述

就醬,OVER!