/** * 餓漢式 * @author yinwei.Liu * */ public class SingletonDemo1 { // 類的靜態成員變量只初始化一次,自然是線程安全的 private static final SingletonDemo1 instance = new SingletonDemo1(); private SingletonDemo1(){} public static SingletonDemo1 getInstance() { return instance; } } /** * 懶漢式 * @author yinwei.Liu * */ public class SingletonDemo2 { // 類初始化時,不初始化這個對象(延遲加載,真正用的時候再建立) private static SingletonDemo2 instance; private SingletonDemo2(){} // 方法同步,調用效率低 public static synchronized SingletonDemo2 getInstance() { if (null == instance) instance = new SingletonDemo2(); return instance; } } /** * 雙重檢查鎖實現 * 將同步放到if內部,提升了執行的效率。 * 沒必要每次獲取對象時都進行同步,只有第一次才同步。 * 建立了之後就沒有必要了。 * 問題: * 因爲編譯器優化緣由和JVM底層內部模型緣由,偶爾會出問題,不建議使用。 * @author yinwei.Liu * */ public class SingletonDemo3 { private static SingletonDemo3 instance = null; private SingletonDemo3(){} public static SingletonDemo3 getInstance() { if (null == instance) { SingletonDemo3 sc; synchronized (SingletonDemo3.class) { sc = instance; if (null == sc) { synchronized (SingletonDemo3.class) { if (null == sc) { sc = new SingletonDemo3(); } } instance = sc; } } } return instance; } } /** * 靜態內部類實現方式(也是一種懶加載方式) * 這種方式:線程安全,調用效率高,而且實現了延遲加載 * @author yinwei.Liu * */ public class SingletonDemo4 { private static class SingletonClassInstance { private static final SingletonDemo4 instance = new SingletonDemo4(); } // 方法沒有同步,調用效率高 public static SingletonDemo4 getInstance() { return SingletonClassInstance.instance; } private SingletonDemo4(){} } /** * 經過枚舉實現單例模式(沒有延遲加載) * 線程安全,調用效率高,不能延遲加載。 * 而且能夠自然的防止反射和反序列化漏洞 * @author yinwei.Liu * */ public enum SingletonDemo5 { // 枚舉元素,自己就是單例對象 INSTANCE; // 能夠添加本身須要的操做 public void singletonOperation() { System.out.println("枚舉類裏面的方法調用"); } } 測試多線程環境下5種建立單例模式的效率 import java.util.concurrent.CountDownLatch; /** * 測試多線程環境下5種建立單例模式的效率 * * @author yinwei.Liu * */ public class Test { public static void main(String[] args) throws Exception { long begin = System.currentTimeMillis(); int threadNum = 100; // 100個線程(10個線程的狀況下,運行屢次有時候耗時爲0!因此讓線程多一點!) final CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100000; i++) { Object obj1 = SingletonDemo1.getInstance(); // 15.餓漢式 // Object obj2 = SingletonDemo2.getInstance(); // 156.懶漢式 // Object obj3 = SingletonDemo3.getInstance(); // 16.雙重檢查鎖,不要使用! // Object obj4 = SingletonDemo4.getInstance(); // 15.靜態內部類 // Object obj5 = SingletonDemo5.INSTANCE; // 16.枚舉實現 } countDownLatch.countDown(); } }).start(); } /* for (int i = 0; i < threadNum; i++) { new Thread(new MyRunnable(countDownLatch)).start(); }*/ countDownLatch.await(); // main線程阻塞,直到計數器變爲0,纔會繼續往下執行 long end = System.currentTimeMillis(); System.out.println("總耗時:" + (end - begin)); } } /*class MyRunnable implements Runnable { private CountDownLatch countDownLatch; public MyRunnable(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { for (int i = 0; i < 100000; i++) { // Object obj1 = SingletonDemo1.getInstance(); // 15.餓漢式 // Object obj2 = SingletonDemo2.getInstance(); // 156.懶漢式 // Object obj3 = SingletonDemo3.getInstance(); // 16.雙重檢查鎖,不要使用! // Object obj4 = SingletonDemo4.getInstance(); // 31.靜態內部類 Object obj5 = SingletonDemo5.INSTANCE; // 16.枚舉實現 } countDownLatch.countDown(); } }*/ /* 選擇哪一種方式實現單例模式?結論: 單例對象 佔用 資源 少,不須要 延遲加載: 枚舉式 好於 餓漢式 單例對象 佔用 資源 大,須要 延遲加載: 靜態內部類式 好於 懶漢式 經常使用的兩種方式,餓漢式和懶漢式,單例對象佔用資源少時,選用餓漢式;反之,用懶漢式。 就效率來講,因爲懶漢式須要同步,效率最低。 若是單例對象佔用資源少,無需延遲加載,使用餓漢式或枚舉式; 若是單例對象佔用資源大,須要延遲加載,使用靜態內部類;*/