1.餓漢式
餓漢式可以保證多線程下唯一的實例,getInstance性能也比較高,但是無法進行懶加載。
/**
* 餓漢式可以保證多線程下唯一的實例,getInstance性能也比較高,但是無法進行懶加載。
*/
public class Singleton1{
private static Singleton1 instance = new Singleton1();
//私有化構造函數,防止被實例化
private Singleton1(){}
public static Singleton1 getInstance(){
return instance;
}
}
2.懶漢式
懶漢式雖然實現單例的懶加載,但是在多線程下不安全的。因爲線程1判斷null == instance爲true時,還沒有* 實例化instance,切換到了線程2運行,線程2判斷null == instance也爲true。就會實例化多次
/**
* 懶漢式雖然實現單例的懶加載,但是在多線程下不安全的。因爲線程1判斷null == instance爲true時,還沒有* 實例化instance,切換到了線程2運行,線程2判斷null == instance也爲true。就會實例化多次
*/
public class Singleton2{
private static Singleton2 instance = null;
//私有化構造函數,防止被外部實例化
private Singleton2(){}
public static Singleton2 getInstance(){
if( instance == null){
instance = new Singleton2;
}
return instance;
}
}
3.懶漢式 + 同步方法(synchronized)
synchronized 控制了整個方法,容易多線程性能低下。
採用懶漢式 + 數據同步方式既滿足了懶加載又能百分之百保證instance實例的唯一性,但是synchronized 關鍵字天生的排他性導致了getInstance方法只能在同一時刻被一個線程所訪問,性能低下
public class Singleton3{
private static Singleton3 instance = null;
//私有化構造函數,防止被外部實例化
private Singleton3(){}
public static synchronized Singleton3 getInstance(){
if( instance == null){
instance = new Singleton3;
}
return instance;
}
}
4.Double-check
double-check 有可能會引起空指針的問題。
關鍵點在於:根據JVM指令重排序和Happens-Before規則,這兩者之間的實例化順序並無前後關係的約束
那麼極有可能instance最先被實例化,而conn並未完成實例化,未完成初始化的實例調用其他方法將會拋出空指針異常。
public class Singleton4{
private static Singleton4 instance = null;
private String conn;
//私有化構造函數,防止被外部實例化
private Singleton4(){
this.conn = "test";
}
public static Singleton4 getInstance(){
if(instance = null){
synchronized(Singleton4.class){
if(instance == null){
instance = new Singleton4();
}
}
}
return instance;
}
}
5.Volatile + double + check
java有用戶內存和主內存的區別,對於變量以volatile 聲明以保證變量在內存的可見性,即可消除這個空指針隱患
public class Singleton5{
private static Singleton5 instance = null;
private String conn;
//私有化構造函數,防止被外部實例化
private Singleton5(){
this.conn = "test";
}
public static Singleton5 getInstance(){
if(instance = null){
synchronized(Singleton5.class){
if(instance == null){
instance = new Singleton5();
}
}
}
return instance;
}
}
6.Holer方式
即以靜態內部類的方式提供示實例
該方法又是同步方法,保證了內存的可見性,JVM的順序性和原子性。Holder方式是單例設計最好的設計之一
public class Singleton6{
//靜態內部類提供實例
private static class Holder{
private static Singleton6 instance = new Singleton6();
}
public static Singleton6 getInstance(){
return Holder.instance;
}
}
7.枚舉方法,線程安全,不能懶加載
枚舉類型不允許被繼承,同樣是線程安全且只能被實例化一次,但是枚舉不能夠懶加載,對於Singleton6主動使用,比如調用其中靜態方法則INSTANCE會立即得到實例化
public enum Singleton7{
INSTANCE;
Singleton7(){}
public static void method(){
//調用該方法,則會主動使用Singleton7, INSTANCE則會被實例化
}
public static Singleton7 getInstance(){
return INSTANCE;
}
}
可改造類似用Holder的方式支持懶加載
public class Singleton7_2{
private Singleton7_2(){}
private enum Holder{
INSTANCE;
private Singleton7_2 instance;
Holder(){
this.instance = new Singleton7_2();
}
public Singleton7_2 getInstance(){
return instance;
}
}
public static Singleton7_2 getInstance(){
return Holder.INSTANCE.getINstance();
}
}