1. 程式人生 > >單例模式的7種實現方式及分析

單例模式的7種實現方式及分析

第一種

  • 程式碼
package com.xiayc.singleton;

/**
 * 餓漢模式
 * @author xyc
 *
 */
public class Hungry {
    private Hungry() {

    }

    private volatile static Hungry singleton = new Hungry();

    public static Hungry getSingleton() {
        return singleton;
    }

    public static void main(String[] args) {
        for
(int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(Hungry.getSingleton()); } }).start(); } } }
  • 執行結果
com.xiayc.singleton.Hungry@1ea452f5
com
.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton.Hungry@1ea452f5 com.xiayc.singleton
.Hungry@1ea452f5
  • 分析

  例項變數的hashCode值一致,說明物件是同一個,餓漢式單例實現是執行緒安全的,
  缺點是該類載入的時候就會直接new 一個靜態物件出來,當系統中這樣的類較多時,會使得啟動速度變慢 ,且不符合懶載入思想。

第二種

  • 程式碼
package com.xiayc.singleton;

/**
 * 懶漢模式
 * @author xyc
 *
 */
public class Lazy {
    private Lazy() {

    }

    private volatile static Lazy singleton = null;

    public static Lazy getSingleton() {
        if(singleton==null) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton = new Lazy();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Lazy.getSingleton());
                }
            }).start();
        }
    }
}
  • 執行結果
com.xiayc.singleton.Lazy@1ea452f5
com.xiayc.singleton.Lazy@7ec5aaaa
com.xiayc.singleton.Lazy@c16150
com.xiayc.singleton.Lazy@1d49826
com.xiayc.singleton.Lazy@423430c8
com.xiayc.singleton.Lazy@58bb45bd
com.xiayc.singleton.Lazy@277ea783
com.xiayc.singleton.Lazy@40d9c054
com.xiayc.singleton.Lazy@27958c7
com.xiayc.singleton.Lazy@50d03d7f
  • 分析

  例項變數的hashCode值不一致,說明物件不是同一個,懶漢式單例實現是非執行緒安全的。
  優點是實現了懶載入思想。

第三種

  • 程式碼
package com.xiayc.singleton;

public class SyncMethodLazy {
    private SyncMethodLazy() {

    }

    private static SyncMethodLazy singleton = null;

    public static synchronized SyncMethodLazy getSingleton() {
        if(singleton==null) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton = new SyncMethodLazy();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SyncMethodLazy.getSingleton());
                }
            }).start();
        }
    }
}
  • 執行結果
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
com.xiayc.singleton.SyncMethodLazy@40d9c054
  • 分析

  例項變數的hashCode值一致,說明物件是同一個,使用synchronized關鍵字的懶漢式單例實現是執行緒安全的。
  優點是實現了執行緒安全並且是懶載入的;缺點是在同一時刻getSingleton方法只能由一個執行緒訪問,效率會很低。

第四種

  • 程式碼
package com.xiayc.singleton;

public class SyncFullCodeBlockLazy {
    private SyncFullCodeBlockLazy() {

    }

    private static SyncFullCodeBlockLazy singleton = null;

    public static SyncFullCodeBlockLazy getSingleton() {
        synchronized (SyncFullCodeBlockLazy.class) {
            if(singleton==null) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new SyncFullCodeBlockLazy();
            }
            return singleton;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SyncFullCodeBlockLazy.getSingleton());
                }
            }).start();
        }
    }
}
  • 執行結果
com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5      com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5      com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5          com.xiayc.singleton.SyncFullCodeBlockLazy@1ea452f5
  • 分析

  例項變數的hashCode值一致,說明物件是同一個,使用synchronized關鍵字修飾全部程式碼塊的懶漢式單例實現是執行緒安全的。
  這種實現方式其實和synchronized修飾方法的實現方式優缺點一致。

第五種

  • 程式碼
package com.xiayc.singleton;

public class SyncPartCodeBlockLazy {
    private SyncPartCodeBlockLazy() {

    }

    private static SyncPartCodeBlockLazy singleton = null;

    public static SyncPartCodeBlockLazy getSingleton() {
        if(singleton==null) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (SyncPartCodeBlockLazy.class) {
                singleton = new SyncPartCodeBlockLazy();
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SyncPartCodeBlockLazy.getSingleton());
                }
            }).start();
        }

    }
}
  • 執行結果
com.xiayc.singleton.SyncPartCodeBlockLazy@7ec5aaaa
com.xiayc.singleton.SyncPartCodeBlockLazy@1d49826   com.xiayc.singleton.SyncPartCodeBlockLazy@277ea783  com.xiayc.singleton.SyncPartCodeBlockLazy@50d03d7f  com.xiayc.singleton.SyncPartCodeBlockLazy@58bb45bd  com.xiayc.singleton.SyncPartCodeBlockLazy@1ea452f5  com.xiayc.singleton.SyncPartCodeBlockLazy@27958c7       com.xiayc.singleton.SyncPartCodeBlockLazy@423430c8      com.xiayc.singleton.SyncPartCodeBlockLazy@40d9c054      com.xiayc.singleton.SyncPartCodeBlockLazy@c16150
  • 分析

  例項變數的hashCode值不一致,說明物件不是同一個,使用synchronized關鍵字修飾區域性程式碼塊的懶漢式單例實現是非執行緒安全的。
  雖然這種實現方法相較於第三種和第四種方式效率要高一些,但並非執行緒安全的。

第六種

  • 程式碼
package com.xiayc.singleton;

public class SyncDoubleCheckLazy {
    private SyncDoubleCheckLazy() {

    }

    private static SyncDoubleCheckLazy singleton = null;

    public static SyncDoubleCheckLazy getSingleton() {
        if(singleton==null) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (SyncDoubleCheckLazy.class) {
                if(singleton==null) {
                    singleton = new SyncDoubleCheckLazy();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SyncDoubleCheckLazy.getSingleton());
                }
            }).start();
        }
    }
}
  • 執行結果
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
  • 分析

  例項變數的hashCode值不一致,說明物件不是同一個,使用synchronized關鍵字雙重檢查的懶漢式單例實現是執行緒安全的。
  其實這種方式是綜合了第三、四、五這三種實現方式,即實現了執行緒安全,也相對提高了執行效率,值得推薦。

第七種

  • 程式碼
package com.xiayc.singleton;

public class StaticInnerClass {
    private StaticInnerClass() {

    }

    private static class StaticInnerClassProvider{  
        private static StaticInnerClass singleton = new StaticInnerClass();  
    }

    public static StaticInnerClass getSingleton() {
        return StaticInnerClassProvider.singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(StaticInnerClass.getSingleton());
                }
            }).start();
        }
    }
}
  • 執行結果
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
com.xiayc.singleton.SyncDoubleCheckLazy@423430c8
  • 分析

  例項變數的hashCode值不一致,說明物件不是同一個,使用靜態內部類方式的懶漢式單例實現是執行緒安全的。
  雖然咋看上去這種方式和第一種餓漢模式的單例模式一樣,兩者都是採用了類裝載的機制來保證初始化例項時只有一個執行緒,但是有區別的地方在於靜態內部類方式在StaticInnerClass類被裝載時並不會立即例項化,而是在需要例項化時,呼叫getInstance方法,才會裝載StaticInnerClassProvider類,從而完成StaticInnerClass的例項化,所以這種方式也值得推薦。