單例模式java
1.1當即加載/餓漢模式安全
當即加載既爲使用類的方法的時候已經將對象建立完畢。常見的實現方法就是直接new實例化。多線程
public class MyObject { private static MyObject myObject = new MyObject(); private MyObject(){} public static MyObject getInstance(){ return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值相同。
1.2延遲加載/懶漢模式線程
延遲加載既爲調用類的方法時,對象實例才被建立。常見實現方法爲在get方法中進行new實例化。code
public class MyObject { private static MyObject myObject; private MyObject(){} public static MyObject getInstance(){ if(myObject == null){ myObject = new MyObject();} return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值不一樣,取出了多個線程實例,與單例模式相違背。
延遲加載的解決方案對象
如上例所示的延遲加載方法,在多線程模式下是線程不安全的,那麼就是如下幾種解決方案吧!get
①聲明synchronized關鍵字同步
既然出現線程不安全問題是由於多個線程同時進入getInstance方法致使的,那麼就給這個方法上鎖,同一時間內只有一個線程可以訪問。hash
public class MyObject { private static MyObject myObject; private MyObject(){} synchronized public static MyObject getInstance(){ if(myObject == null){ myObject = new MyObject(); } return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值相同。
可是這種方法的運行效率很是低下。class
②嘗試同步代碼塊
public class MyObject { private static MyObject myObject; private MyObject(){} public static MyObject getInstance(){ synchronized (MyObject.class){ if(myObject == null){ myObject = new MyObject();} } return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值相同。
可是這至關於第一種給方法上鎖的方式,效率一樣低下。
③嘗試給重要代碼上鎖
public class MyObject { private static MyObject myObject; private MyObject(){} public static MyObject getInstance(){ if(myObject == null){ synchronized (MyObject.class){ myObject = new MyObject(); } } return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值不相同,並非單例的。
這樣的上鎖方式雖然雖然提升了效率,可是仍有可能同時有多個線程進入if方法內部,等待執行同步代碼塊,所以獲取了多個對象。
④使用DCL雙檢查鎖機制
public class MyObject { private static MyObject myObject; private MyObject(){} public static MyObject getInstance(){ if(myObject == null){ synchronized (MyObject.class){ if(myObject == null){ myObject = new MyObject(); } } } return myObject; } } public class MyThread extends Thread { public void run(){ System.out.println(MyObject.getInstance().hashCode()); } public static void main(String [] args){ MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } } 運行結果爲三個線程輸出的值相同。
使用雙重檢查鎖機制,成功的解決了懶漢模式遇到多線程的問題。DCL也是大多數多線程結合單例模式使用的解決方案。