單例模式的5種實現方式,以及在多線程環境下5種建立單例模式的效率

/** 
 * 餓漢式 
 * @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(); 
    } 
}*/  

 

 /*

選擇哪一種方式實現單例模式?結論:

單例對象 佔用 資源 少,不須要 延遲加載:
枚舉式 好於 餓漢式
單例對象 佔用 資源 大,須要 延遲加載:
靜態內部類式 好於 懶漢式

 
經常使用的兩種方式,餓漢式和懶漢式,單例對象佔用資源少時,選用餓漢式;反之,用懶漢式。

就效率來講,因爲懶漢式須要同步,效率最低。

若是單例對象佔用資源少,無需延遲加載,使用餓漢式或枚舉式;

若是單例對象佔用資源大,須要延遲加載,使用靜態內部類;*/
相關文章
相關標籤/搜索