Java多執行緒環境下的懶漢模式解決方案
阿新 • • 發佈:2018-12-14
一、場景簡述
單例模式下有餓漢模式和懶漢模式,其中懶漢模式在於呼叫相關方法時例項才被建立。懶漢模式我們不難實現,但是在懶漢模式下我們如果使用多執行緒,就會取出多個例項的情況,與單例模式相違背,所以該篇部落格筆者主要關於在多執行緒環境下利用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》