1. 程式人生 > >Java多執行緒環境下的懶漢模式解決方案

Java多執行緒環境下的懶漢模式解決方案

一、場景簡述

單例模式下有餓漢模式和懶漢模式,其中懶漢模式在於呼叫相關方法時例項才被建立。懶漢模式我們不難實現,但是在懶漢模式下我們如果使用多執行緒,就會取出多個例項的情況,與單例模式相違背,所以該篇部落格筆者主要關於在多執行緒環境下利用DCL雙檢查鎖機制來實現懶漢模式。

二、場景實現

1、多執行緒環境下的懶漢模式實現“錯誤的單例模式”

MyObject類

package singleton;

/**
 * @author: linjie
 * @description: 懶漢模式
 * @create: 2018/10/07 13:29
 */
public class MyObject {
    private static MyObject myObject;
    private MyObject(){}
    public static MyObject getInstance(){
        try {
            //懶漢模式
            if (myObject != null){
            }else {
                //模擬建立物件之前做的準備性工作
                Thread.sleep(3000);
                myObject = new MyObject();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return myObject;
    }
}

MyThread類

package singleton;

/**
 * @author: linjie
 * @description: 執行緒類
 * @create: 2018/10/07 13:32
 */
public class MyThread extends Thread{
    @Override
    public void run(){
        //列印hashcode值
        System.out.println(MyObject.getInstance().hashCode());
    }
}

Run啟動類(實現多個執行緒)

package singleton;

/**
 * @author: linjie
 * @description:啟動類
 * @create: 2018/10/07 13:34
 */
public class Run {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        myThread.start();
        myThread1.start();
        myThread2.start();
    }
}

執行結果,可以看到是不同的hashcode值,違背了單例模式

2、使用synchronized同步方法/同步程式碼塊實現多執行緒下的懶漢模式

只需修改MyObject類

使用同步方法

package singleton;

/**
 * @author: linjie
 * @description: 懶漢模式
 * @create: 2018/10/07 13:29
 */
public class MyObject {
    private static MyObject myObject;
    private MyObject(){}
    synchronized public static MyObject getInstance(){
        try {
            //懶漢模式
            if (myObject != null){
            }else {
                //模擬建立物件之前做的準備性工作
                Thread.sleep(3000);
                myObject = new MyObject();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return myObject;
    }
}

使用同步程式碼塊

package singleton;

/**
 * @author: linjie
 * @description: 懶漢模式
 * @create: 2018/10/07 13:29
 */
public class MyObject {
    private static MyObject myObject;
    private MyObject(){}
    public static MyObject getInstance(){
        try {
            synchronized (MyObject.class){
                if (myObject != null){
                }else {
                    //模擬建立物件之前做的準備性工作
                    Thread.sleep(3000);
                    myObject = new MyObject();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return myObject;
    }
}

然而通過以上兩種方法的確實現了單例,獲取的hashcode也是同一個值,但是他們這樣的寫法,是全部程式碼都是同步的,這就大大降低了執行效率,所以才有某些重要的程式碼進行單獨的同步,而其他程式碼則不需要同步,這樣執行效率可以大大提升,但是會導致無法解決得到同一個例項物件的結果,如下

package singleton;

/**
 * @author: linjie
 * @description: 懶漢模式
 * @create: 2018/10/07 13:29
 */
public class MyObject {
    private static MyObject myObject;
    private MyObject(){}
    public static MyObject getInstance(){
        try {
            //懶漢模式
            if (myObject != null){
            }else {
                //模擬建立物件之前做的準備性工作
                Thread.sleep(3000);
                //雖然部分程式碼被上鎖,但還是有非執行緒安全的問題
                synchronized (MyObject.class){
                    myObject = new MyObject();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return myObject;
    }
}

執行結果,即使效率提升,但還是違背了單例模式

3、使用DCL雙檢查鎖機制實現多執行緒下的懶漢模式

所以我們就使用DCL雙檢查鎖機制來實現多執行緒環境下的懶漢模式了

MyObject類

package singleton;

/**
 * @author: linjie
 * @description: 此版本程式碼稱為雙重檢查 Double-Check Locking
 * @create: 2018/10/07 13:50
 */
public class MyObject {
    private static MyObject myObject;
    private MyObject(){}
    public static MyObject getInstance(){
        try {
            if (myObject != null){
            }else {
                //模擬建立物件之前做的準備性工作
                Thread.sleep(3000);
                //同步程式碼塊中還有一層判斷
                synchronized (MyObject.class){
                    if (myObject == null){
                        myObject = new MyObject();
                    }
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        return myObject;
    }
}

MyThread類

package singleton;

/**
 * @author: linjie
 * @description: 執行緒類
 * @create: 2018/10/07 13:32
 */
public class MyThread extends Thread{
    @Override
    public void run(){
        //列印hashcode值
        System.out.println(MyObject.getInstance().hashCode());
    }
}

Run啟動類

package singleton;

/**
 * @author: linjie
 * @description:啟動類
 * @create: 2018/10/07 13:34
 */
public class Run {
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        myThread.start();
        myThread1.start();
        myThread2.start();
    }
}

執行結果,可以看到使用了DCL雙檢查機制後,實現了單例的結果,並且是部分程式碼同步,大大提升了執行效率,何樂而不為。

三、參考文獻

《Java Multi-thread Programming》