單例模式一:飢餓模式
/**
* 飢餓模式
*
* 不存在併發問題
* @author [email protected]
* @date 2020/3/6 0006 下午 9:12
*/
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton(){
System.out.printf("%s: construct called....%n",getClass().getSimpleName());
}
public static EagerSingleton getInstance(){
return INSTANCE;
}
public void sing(){
System.out.printf("%s: Sing....%n",getClass().getSimpleName());
}
}
單例模式二:DLC模式
即懶加載模式中的double lock check模式
/**
* double lock check模式
* @author [email protected]
* @date 2020/3/6 0006 下午 9:13
*/
public class DoubleLockSingleton {
//必須使用volatile 是因爲cpu有可能指令re-order,
//此關鍵字會插入內存屏障,阻止cpu re-order
private volatile static DoubleLockSingleton INSTANCE = null;
private DoubleLockSingleton(){
System.out.printf("%s: construct called....%n",getClass().getSimpleName());
}
public static DoubleLockSingleton getInstance(){
//double check null
if(INSTANCE == null){
synchronized (DoubleLockSingleton.class){
//must check again
//because maybe another thread initialize it already
if(INSTANCE == null){
INSTANCE = new DoubleLockSingleton();
}
}
}
return INSTANCE;
}
public void sing(){
System.out.printf("%s: Sing....%n",getClass().getSimpleName());
}
}
單例模式三:靜態內部類模式
優點:不需要double lock check
/**
* When the singleton class is loaded,
* inner class is not loaded and hence doesn’t create object when loading the class.
* Inner class is created only when getInstance() method is called.
* So it may seem like eager initialization but it is lazy initialization.
* This is the most widely used approach as it doesn’t use synchronization.
* @author [email protected]
* @date 2020/3/6 0006 下午 9:16
*/
public class InnerClassSingleton {
private InnerClassSingleton(){
System.out.printf("%s: construct called....%n",getClass().getSimpleName());
}
private static class Holder{
static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return Holder.INSTANCE;
}
public void sing(){
System.out.printf("%s: Sing....%n",getClass().getSimpleName());
}
}
單例模式四:枚舉模式
/**
*
* 這一種是最好的
* 比InnerClassSingleton還好一點,因爲不用再定義一些不必須的屬性和內部類
*
* @author [email protected]
* @date 2020/3/6 0006 下午 9:30
*/
public enum EnumSingleton {
INSTANCE;
private EnumSingleton(){
System.out.printf("%s: construct called....%n",getClass().getSimpleName());
}
//etc
private int propertyA;
//etc....
public void sing(){
System.out.printf("%s: Sing....%n",getClass().getSimpleName());
}
}
優劣比較
以前四種寫法均不存在併發問題,枚舉與飢餓模式寫法最簡便,個人比較推薦枚舉模式。
測試類
/**
* test for singleton
*
* java -verbose:class SingletonBoot
*
* @author [email protected]
* @date 2020/3/11 0011 上午 11:22
*/
public class SingletonBoot {
public static void main(String[] args) {
System.out.printf("%nBEGIN%n%n");
EagerSingleton.getInstance().sing();
DoubleLockSingleton.getInstance().sing();
InnerClassSingleton.getInstance().sing();
EnumSingleton.INSTANCE.sing();
System.out.printf("%nEND%n%n");
}
}
執行結果
BEGIN
[Loaded com.david.learning.concurrent.singleton.EagerSingleton]
[Loaded java.util.Formattable from rt.jar]
EagerSingleton: construct called....
EagerSingleton: Sing....
[Loaded com.david.learning.concurrent.singleton.DoubleLockSingleton]
DoubleLockSingleton: construct called....
DoubleLockSingleton: Sing....
[Loaded com.david.learning.concurrent.singleton.InnerClassSingleton]
[Loaded com.david.learning.concurrent.singleton.InnerClassSingleton$Holder]
InnerClassSingleton: construct called....
InnerClassSingleton: Sing....
[Loaded com.david.learning.concurrent.singleton.EnumSingleton]
EnumSingleton: construct called....
EnumSingleton: Sing....
END
澄清
飢餓模式寫法與DLC模式相比較並不比DLC耗資源,雖然它是飢餓模式,但其也是在使用這個類的地方纔會加載(引用成員或使用方法時初始化)它(變相懶加載)。
如有謬誤,請各位網友不吝指教。