單例模式的六種實作方式
1、單例模式之餓漢式[可用]
//餓漢式實作單例模式
public class Hungry {
private Hungry(){}
private static Hungry hungry = new Hungry();
public static Hungry getHungry(){
return hungry;
}
}
優點:實作簡單,類加載的時候就完成了實體化,避免了執行緒的同步問題
缺點:無法實作延遲加載,可能會造成記憶體的浪費(浪費可忽略)
2、單例模式之懶漢式[不可用]
//懶漢式實作單例模式
public class LazySingleton01 {
private LazySingleton01(){}
private static LazySingleton01 lazy ;
public static LazySingleton01 getLazy(){
if(lazy==null){
return new LazySingleton01();
}
return lazy;
}
}
缺點:執行緒不安全,不推薦使用
存在執行緒安全問題的程序:在運行程序中可能會存在這么一種情況,有多個執行緒去呼叫getLazy方法來獲取LazySingleton01實體,就有可能發生,第一個執行緒在執行if(lazy = =null)的陳述句時,還沒執行new LazySingleton01()時,第二個執行緒獲得的lazy也為null,通過了(lazy==null)的判斷,最終兩個執行緒都會new實體,
3、單例模式之懶漢式+同步方法[不推薦使用]
//懶漢式+同步方法實作單例模式
public class LazySingleton02 {
private LazySingleton02(){}
private static LazySingleton02 lazy;
public static synchronized LazySingleton02 getLazy(){
if(lazy==null){
return new LazySingleton02();
}
return lazy;
}
}
執行緒安全,效率低不推薦使用
對于上述缺陷的改進可能有的人會想到如下的代碼:
public class LazySingleton03 {
private LazySingleton03(){}
private static LazySingleton03 lazy;
public static LazySingleton03 getLazy(){
if(lazy==null){
synchronized(LazySingleton03.class){
return new LazySingleton03();
}
}
return lazy;
}
}
該寫法一樣是執行緒不安全的,當一個執行緒還沒有實體化LazySingleton03時,另一個執行緒執行到if(lazy==null)這個判斷陳述句時就會進入if陳述句,雖然加了鎖,但是等到第一個執行緒執行完lazy=new LazySingleton03()釋放出這個鎖時,另一個進入if陳述句的執行緒同樣會實體化另外一個LazySingleton03物件,
經過一步步的探索,有了雙重校驗鎖的懶漢式寫法:
4、單例模式之懶漢式+雙重校驗鎖[推薦使用]
//雙重校驗鎖+懶漢式
public class DoubleCheckSingleton {
private DoubleCheckSingleton(){}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
優點:執行緒安全,延遲加載,效率較高
注意此處的volatile:
//雙重校驗鎖+懶漢式
public class DoubleCheckSingleton {
private Socket socket;
private DoubleCheckSingleton(){
this.socket=new Socket();
}
private volatile static DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance(){
if(instance==null){
synchronized (DoubleCheckSingleton.class){
if(instance==null){
return new DoubleCheckSingleton();
}
}
}
return instance;
}
}
根據JVM運行時指令重排序和Happens-Before的規則,instance和socket之間的實體化的順序并沒有什么關系約束,誰在前誰在后都有可能,如果instance最先實體化,那么socket就不會實體化,呼叫這個方法就會產生空指標例外------>
解決這個方法很簡單,加一個關鍵字 volatite
這個關鍵字能夠保證實體化的順序的相對位置不變,instance實體化永遠在socket之后
5、單例模式之靜態內部類[推薦使用]
//靜態內部類實作單例模式
public class StaticInner {
private StaticInner(){}
private static class inner{
private static StaticInner instance=new StaticInner();
}
public static StaticInner getInstance(){
return inner.instance;
}
}
優點:避免了執行緒不安全,延遲加載,效率高
詳解:這個方法并沒有事先宣告instance的靜態成員,而是把它放到了靜態內部類inner中,inner中定義了靜態變數,并直接進行了實體化,當inner被主動參考的時候就會創建實體,StaticInner在實體創建程序中被收集到()方法中,這個方法是同步方法,
保證了記憶體的可見性,JVM指令的順序執行和原子性,該方法也是單例模式的最好設計之一,
6、單例模式之列舉[推薦使用]
//列舉實作單例模式
public enum EnumSingleton {
INSTANCE;
private EnumSingleton(){}
public void method(){}
}
借助JDK1.5中添加的列舉來實作單例模式
優點:不僅能避免多執行緒同步問題,而且還能防止反序列化重新創建新的物件,實作非常簡單而且最安全可謂很完美,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/190944.html
標籤:java
下一篇:JAVA學習日記: 執行緒(5)
