God类里面封装一个God自己,对,一切都是神创造的,包括我们人类。有人开始质疑,那神是谁?神自己是谁造的?
public class God {
private static final God god = new God();//自有永有的神单例
private God(){}//构造方法私有化
}
以上private关键字保证了上帝的私有性,不可见性,不可访问性,我想没有活人见过上帝吧?static关键字保证上帝的静态性,他与类同在,不依赖于类的实例化就自有永有,他将在内存中永生,GC垃圾回收器也回收不了他。final关键字则保证这位神是和常量,衡量,他是终极上帝,不能再改。
正如同静态方法main(),不需要实例化类就能运行的入口,同样我们需要一个静态方法getInstance()来请神,方法体内我们就返回这个在唯一的真神,当然方法它必须是public公开的,不然谁都访问不了。
1、痴汉模式:
public class God {
private static final God god = new God();//自有永有的神单例
private God(){}//构造方法私有化
public static God getInstance(){//请神方法公开化
return god;
}
}
那对于外部来说只要调用God.getInstance();就可以拿到神了,而且不管谁拿,拿几次,都是同一个神,这样就保证了整个系统中神的唯一性,不可伪造性
代码第一行一开始就造出了神,已经准备好了随时给你请神,这样就有了一个问题,如果没人请神那不是白造了?提前备货如果价格跌了不是很惨?反应在系统中的问题就是占用了内存空间。
2、“懒汉模式(Lazy load)”
public class God {
private static God god;//这里不进行实例化
private God(){}
public static God getInstance() {
if (god == null) {//如果无神才造神
god = new God();
}
return god;
}
}
这我们看到一开始就没有造神,只有某人第一次求神时才实例化,之后再求的就直接返回了。这样的好处是省了一段时间的内存(无求神期间),坏处是第一次请神的时候速度相较之前的痴汉模式会慢,因为要消耗CPU去造神。
其实这么写是在多线程模式下是有陷阱的,试想多人同时并发请神的话,依然会造成多神,好吧我们再来改良一下,把请神方法加上synchronized,声明为同步方法,某线程调用前必须获取同步锁,调用完后会释放锁给其他线程用,也就是请神的必须排队,大家一个一个按顺序来。
public class God {
private static God god;//这里不进行实例化
private God(){}
public static synchronized God getInstance() {//此处加入同步
if (god == null) {//如果无神才造神
god = new God();
}
return god;
}
}
然而,这样做是要付出代价的,还没进庙呢不管三七二十一请神的直接给加锁排队,结果队伍从北边的庙排到了南天门,人们都要来一个一个拜佛求神,这造成了巨大时间浪费,没有充分利用CPU资源并发优势(特别是多核情况)。好吧,那还是让人们抢好了,但依然得保证单例神的情况下。
这里我们去掉方法上的同步关键字,换到方法体内部做同步,整个方法开放并发大家都可以同时入庙,当然起早贪黑的虔诚信徒们要抢头香是必须要入堂排队的。一旦头香诞生,那其他抢香的都白早起,白排队了。再之后的事情我们都可以预见了,头注香被抢后堂内排队再无必要来了,大家可以在堂外同时并发拜佛求神,这就极大的利用了CPU资源。简而言之:只有第一批抢头香的在排队,之后大家都不必排队了,代码如下。
3、多线程下安全模式
public class God {
private volatile static God god;
private God(){}
public static God getInstance() {//庙是开放的不用排队进入
if (god == null) {//如果头柱香未产生,这批抢香人进入堂内排队。
synchronized(God.class){
if (god == null) {//只有头香造了神,其他抢香的白排队了
god = new God();
}
}
}
//此处头柱香产生后不必再排队
return god;
}
}
对于我个人来讲倾向于痴汉模式,现在内存成本根本不算问题,况且迟早要被实例化占用内存,加锁解锁更是一种浪费,还有同步效率低等问题,如果上帝不是很占空间那就没必要去懒汉延迟加载,越复杂问题越多,风险越大。