餓漢式
//餓漢式單例 可能會(huì)浪費(fèi)空間 還沒(méi)使用的時(shí)候已經(jīng)初始化了
//單例自己實(shí)例化自己,構(gòu)造器必須私有
public class Hungry {
private Hungry(){
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
懶漢式DCL (double-check-lock)
//懶漢式單例
public class Lazzy {
private Lazzy(){
System.out.println(Thread.currentThread().getName()+":ok");
}
public volatile static Lazzy lazzy;
//雙檢鎖 DCL
public static Lazzy getInstance(){
if(lazzy == null){
synchronized (Lazzy.class){
if(lazzy == null){
lazzy = new Lazzy();
//new 一個(gè)對(duì)象非原子性操作颜骤,可能會(huì)有指令重排的現(xiàn)象
/**
new一個(gè)對(duì)象的過(guò)程
1:分配內(nèi)存空間
2:執(zhí)行構(gòu)造方法筒严,初始化對(duì)象
3:把對(duì)象指向1的空間
執(zhí)行順序123榕订,也有可能順序是 132岳瞭;
A線程執(zhí)行完 1搔驼、3 的時(shí)候鹃彻;
B線程進(jìn)來(lái)了,會(huì)發(fā)現(xiàn) 第一個(gè)判斷 lazzy != null, 會(huì)直接返回一個(gè)未初始化完成的對(duì)象筒占;
所以單例使用雙檢鎖酒甸,要避免當(dāng)前對(duì)象指令重排;使用volatile
*/
}
}
}
return lazzy;
}
// //單線程環(huán)境下沒(méi)問(wèn)題
// public static Lazzy getInstance(){
// if(lazzy == null){
// //多個(gè)線程代碼可以同時(shí)進(jìn)入這里
// lazzy = new Lazzy();
// }
// return lazzy;
// }
public static void main(String[] args) {
for (int i=0; i<100; i++) {
new Thread(()->{
Lazzy.getInstance();
}).start();
}
}
public class Holder {
//靜態(tài)內(nèi)部類實(shí)現(xiàn)單列
private Holder() {
}
public static Holder getInstance(){
return InnerHolder.holder;
}
public static class InnerHolder{
private static final Holder holder = new Holder();
}
}
以上都可以被反射機(jī)制破壞 反射可以拿到private的空參構(gòu)造方法赋铝,改變私有權(quán)限
//反射插勤!
public static void main(String[] args) throws Throwable {
Lazzy instance = Lazzy.getInstance();
Constructor<Lazzy> declaredConstruct = Lazzy.class.getDeclaredConstructor(null);
declaredConstruct.setAccessible(true);
Lazzy instance2 = declaredConstruct.newInstance();
}
//枚舉單例 枚舉實(shí)質(zhì)上也是個(gè)class是天然的單例, 繼承了Enum
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
枚舉為什么不能被反射破壞?
答:jdk的源碼有限制,不能以反射的方式創(chuàng)建枚舉 Cannot reflectively create enum objects
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
- 為什么有人說(shuō) 實(shí)現(xiàn)接口是枚舉單例的最佳方式?农尖?
// 定義單例模式中需要完成的代碼邏輯
public interface MySingleton {
void doSomething();
}
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("complete singleton");
}
};
public static MySingleton getInstance() {
return Singleton.INSTANCE;
}
}