單例模式的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的例項化,所以這種方式也值得推薦。