1. 程式人生 > >Java設計模式學習01——單例模式(轉)

Java設計模式學習01——單例模式(轉)

多線程 調用 直接 private 延時加載 stream 構造 漏洞 天然

原地址:http://blog.csdn.net/xu__cg/article/details/70182988

Java單例模式是一種常見且較為簡單的設計模式。單例模式,顧名思義一個類僅能有一個實例,並且向整個系統提供這一個實例。
單例模式的特點:

  • 單例類僅能有一個實例。
  • 單例類必須為自己創建實例。
  • 單例類必須向外界提供獲取實例的方法。
    以下是幾種實現方法

一、懶漢式單例(能夠延時加載)

public class SingleTon {
     private static SingleTon instance = null ;
     private SingleTon(){   
     }
     public static SingleTon getInstance(){
         if(instance==null){
             instance=new SingleTon();
        }
         return instance ;
    }
}   

通過是構造方法私有化使該類不能在外界實例化,只能通過public方法獲取實例,從而保證實例唯一。
但是以上的代碼是線程不安全的,多線程並發的情況下,SingleTon可能產生多個實例,可以通過以下方法對getInstance方法進行改進從而保證懶漢式單例線程安全。

1.在getInstance()方法上加上同步

public class SingleTon {
     private static SingleTon instance = null ;
     private SingleTon(){

    }
     //方法同步,調用效率低!
     public static synchronized SingleTon getInstance(){
         if(instance==null){
             instance=new SingleTon();
        }
         return instance ;
    }
}   

2.靜態內部類式

public class SingleTon {  
     private static class SingletonClassInstance {
         private static final SingleTon instance=new  SingleTon();
    }
     private SingleTon(){    
    }
     //方法沒有同步,調用效率高!
     public static SingleTon getInstance(){
         return SingletonClassInstance.instance ;
    }
}   

3.雙重校驗鎖式

public class SingleTon {
     private volatile static SingleTon instance ;

     private SingleTon(){

    }
     public static SingleTon getInstance(){
         if(instance==null){
             synchronized(SingleTon.class ){
                 if(instance==null){
                     instance=new SingleTon();
                }
            }
        }
         return   instance ;
    }
}

二、餓漢式單例 (不能夠延時加載)

public class SingleTon {
     //類初始化時,立即加載這個對象,加載類時,天然的是線程安全的!
     private static SingleTon instance=new SingleTon();

     private SingleTon(){

    }
     //方法沒有同步,調用效率高!
     public static SingleTon getInstance(){
         return instance ;
    }
}   

餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。

三、枚舉式單例

public enum SingleTon {

     //這個枚舉元素,本身就是單例對象!
     INSTANCE;

     //添加自己需要的操作!
     public void singletonOperation(){
    }

}   

枚舉式單例是線程安全的,調用效率高,並且可以天然的防止反射和反序列化漏洞。

四、防止反射和反序列化


事實上,通過Java反射或反序列化能夠獲取構造方法為private的類實例,那麽所有的單例都會失效。所以為了避免這種後果,需要采取相應措施。

/**
 *懶漢式單例模式(如何防止反射和反序列化漏洞)
 *
 */
public class SingleTon implements Serializable {
     //類初始化時,不初始化這個對象(延時加載,真正用的時候再創建)。
     private static SingleTon instance ;  

     private SingleTon(){  //私有化構造器
         if(instance!=null){
             throw new RuntimeException(); //防止反射
        }
    }

     //方法同步,調用效率低!
     public static synchronized SingleTon getInstance(){
         if(instance==null){
             instance=new SingleTon();
        }
         return instance ;
    }

     //反序列化時,如果定義了readResolve()則直接返回此方法指定的對象。而不需要單獨再創建新對象!
     private Object readResolve() throws ObjectStreamException {
         return instance ;
    } }   

五、如何選用單例模式實現方式

  • 單例對象占用資源少,且不需要延時加載:枚舉式 好於 餓漢式
  • 單例對象占用資源大,且需要延時加載:靜態內部類式 好於 一般懶漢式

Java設計模式學習01——單例模式(轉)