這里我們介紹一下在多線程中如何安全正確的編寫單例模式的代碼。不知為何,恰如其分的話總是姍姍來遲,錯過最恰當的時機。
多線程中的單例模式
這里面通過代碼來體會一下在多線程中如何正確的編寫單例模式的代碼。相同的代碼如下,不同的是Object這個類。
package com.linux.huhx.thread3.singleDesign_1; /** * @Author: huhx * @Date: 2017-10-31 下午 4:28 */ public class SingleDesignTest1 { public static void main(String[] args) { MyThread[] threads = new MyThread[10]; for (int i = 0; i < 10; i++) { threads[i] = new MyThread(); } for (int i = 0; i < 10; i++) { threads[i].start(); } } static class MyThread extends Thread { @Override public void run() { System.out.println(MyObject*.getInstance().hashCode()); } } }
以下的不同測試類的結果,都是基於修改MyThread里面run方法的MyObject*的值。
一、立即加載方式(餓漢模式)
public class MyObject1 { private static MyObject1 myObject = new MyObject1(); private MyObject1() {} public static MyObject1 getInstance() { return myObject; } }
安全:一次的打印結果如下
1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770 1508118770
二、延遲加載方式(懶漢模式)
public class MyObject2 { private static MyObject2 myObject; private MyObject2() {} public static MyObject2 getInstance() { try { if (myObject == null) { // 模擬一些准備的耗時操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject2(); } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
不正確:一次的打印結果
401241377 355159803 1875573029 797782832 1696014491 1060834655 961745937 1060834655 1312341120 985396398
三、延遲加載解決方案之聲明synchronized
public class MyObject3 { private static MyObject3 myObject; private MyObject3() {} public synchronized static MyObject3 getInstance() { try { if (myObject == null) { // 模擬一些准備的耗時操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject3(); } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印結果
774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507 774263507
效率比較低下:同步運行,下一個線程想要取得對象,則必須等待上一個線程釋放鎖之后,才可以繼續執行。
四、延遲加載解決方案之同步代碼塊
public class MyObject4 { private static MyObject4 myObject; private MyObject4() {} public static MyObject4 getInstance() { try { synchronized (MyObject4.class) { if (myObject == null) { // 模擬一些准備的耗時操作 TimeUnit.SECONDS.sleep(2); myObject = new MyObject4(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印結果如下
1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392 1373650392
效率比較低下:和上述的synchronized同步方法一樣都是同步運行的。
五、延遲加載解決方案之同步部分代碼塊
public class MyObject5 { private static MyObject5 myObject; private MyObject5() {} public static MyObject5 getInstance() { try { if (myObject == null) { // 模擬一些准備的耗時操作 TimeUnit.SECONDS.sleep(2); synchronized (MyObject5.class) { myObject = new MyObject5(); } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
不安全:一次的打印結果如下
401241377 1696014491 797782832 1875573029 1060834655 43626537 1971360294 1312341120 961745937 860826410
六、延遲加載解決方案之DCL雙檢查鎖機制
public class MyObject6 { private volatile static MyObject6 myObject; private MyObject6() {} public static MyObject6 getInstance() { try { if (myObject == null) { // 模擬一些准備的耗時操作 TimeUnit.SECONDS.sleep(2); synchronized (MyObject6.class) { if (myObject == null) { myObject = new MyObject6(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return myObject; } }
安全:一次的打印結果如下
1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120 1312341120
使用雙重檢查鎖功能,成功地解決了“懶漢模式”遇到多線程的問題。DCL也是大多數多線程結合單例模式使用的解決方案。
七、使用靜態內置類實現單例模式
public class MyObject7 { private static class MyObjectHandler { private static MyObject7 myObject = new MyObject7(); } private MyObject7() {} public static MyObject7 getInstance() { return MyObjectHandler.myObject; } }
安全:一次的打印結果如下
1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261 1240673261
八、使用static代碼塊實現單例模式
public class MyObject8 { private static MyObject8 myObject = null; static { myObject = new MyObject8(); } private MyObject8() {} public static MyObject8 getInstance() { return myObject; } }
安全:一次的打印結果如下
1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029 1875573029
友情鏈接