单例模式:在整个项目中,只产生一个实例化对象,这类主要由数据库连接池等,对于这类对象,一定要小心使用, 因为一般而言单例对象在整个项目中存活很久。有甚者贯穿了这个项目生命周期。定义全局的集合的时候,一定要小心集合内的元素,有可能存储在集合的对象在程序中没有作用,但在集合内存在强引用而造成内存泄漏。
下面就来介绍几种单例模式的写法。
饿汉式
/**
* 饿汉式:在类加载期间就完成单例对象的绑定。由于在类加载期间是线程安全的,所以饿汉式是不会发生线程安全问题的。
*/
class SigletonObject{
public static SigletonObject sigletonObject = new SigletonObject();
private SigletonObject() {}
public static SigletonObject getInstance() {
return sigletonObject;
}
}
懒汉式
再不是并发场景下,懒汉式
class SigletonObject{
public static SigletonObject sigletonObject = null;
private SigletonObject() {}
public static SigletonObject getInstance() {
if (sigletonObject == null) {
sigletonObject = new SigletonObject();
}
return sigletonObject;
}
}
但是上面这段代码在多线程环境下,就可能产生多个对象,这对于单例模式是不可忍受的。
class SigletonObject{
public static SigletonObject sigletonObject = null;
private SigletonObject() {}
public static synchronized SigletonObject getInstance() {
if(sigletonObject == null) {
sigletonObject = new SigletonObject();
}
return sigletonObject;
}
}
双重锁
这段代码虽然是线程安全,但即使对象已经创建成功,获取该对象都要获取锁,这是不能忍的的。
class SigletonObject {
public static SigletonObject sigletonObject = null;
private SigletonObject() {
}
public static SigletonObject getInstance() {
if (sigletonObject == null) {
synchronized (SigletonObject.class) {
if (sigletonObject == null) {
sigletonObject = new SigletonObject();
}
}
}
return sigletonObject;
}
}
双重锁判断,不管你的并发度多高,他都能满足要求,但是同样,他是有可能暴露出还没初始化完成的对象,这是不能忍的。
而造成这个问题的是一般在对象生成的中,首先是分配地址,初始化对象,这是没问题的,但是把初始化的对象地址赋给sigletonObject和初始化对象存在指令重排序问题,这就会造成把没有初始化对象给sigletonObject,这就会造成问题。
class SigletonObject {
public static volatile SigletonObject sigletonObject = null;
private SigletonObject() {
}
public static SigletonObject getInstance() {
if (sigletonObject == null) {
synchronized (SigletonObject.class) {
if (sigletonObject == null) {
sigletonObject = new SigletonObject();
}
}
}
return sigletonObject;
}
}
这段代码就完美了。
静态内部类
class SigletonObject{
private SigletonObject() {}
static class inner{
private static SigletonObject sigletonObject = new SigletonObject();
}
public static SigletonObject getInstance() {
return inner.sigletonObject;
}
}
在加载SigletonObject类时,不会加载其内部类,只有调用inner.sigletonObject才会真正加载内部类,这就可以满足懒加载,这也是作者推荐使用的。但是他也有不适合的场景,虽然我们私有化了他们的构造函数,但是如果是通过反射获取对象的话,这就会造成产生多个对象,所以如果项目需要通过反射获取单例对象,就不适用了。
枚举
class SigletonObject{
private SigletonObject() {}
private enum SigletonEnum{
INSTANCE;
private SigletonObject sigletonObject;
private SigletonEnum() {
this.sigletonObject = new SigletonObject();
}
public SigletonObject getInstance() {
return sigletonObject;
}
}
public static SigletonObject getInstance() {
return SigletonEnum.INSTANCE.getInstance();
}
}
如果说有一种让作者都心服口服的,那就是枚举实现单例模式,虽然枚举现在很少使用,但确实是好。