特点:
1.构造函数不外放,一般都是private
2.通过暴露一个public的静态方法或者枚举来返回一个单例对象
3.在多线程环境下面,确保单例类对象有且只有一个
4.确保单例类对象再反序列化时候不会重新构建对象
懒汉模式:
public classSingleton{ private staticSingleton instance; //1.构造函数不对外开放
privateSingleton{ } //2.通过一个静态方法来返回单例类对象 //3.加了synchronized,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized修饰的方法或者代码块。 //也就是getInstance是一个同步方法,保证多线程下对象唯一
public static synchronizedSingleton getInstance(){ if(instance == null){ instance = newSingleton(); } returninstance; }
}
优点:需要时候才实例化,节约资源
缺点:每次调用getInstance()都要同步,造成不必要的同步开销
Double checkLock实现单例:
public classSingleton{ private volatile staticSingleton singleton; privateSingleton (){} public staticSingleton getInstance() { //第一层判空是为了避免不必要的同步 //从而只有第一次的时候才会加锁,之后就不会加锁
if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { //第二层判空是为了在null的情况下创建实例
singleton = newSingleton(); } } } returnsingleton; } }
注意DCL是有问题的:
singleton = new Singleton();
这句并不是原子操作,它可以拆分三件事:
(1).给Singleton的实例分配内存
(2).调用Singleton的构造函数,创建对象
(3).将singleton的对象指向内存分配的空间
但是,由于Java编译器允许处理器乱序执行,以及JDK1.5之前JVM中Cache,寄存器以及主内存回写顺序的规定,(2)与(3)的顺序不是固定的,
所以可能执行顺序是123,也可能是132
如果是132,那么此时比如A线程执行到13,还未执行2,此时B线程也在执行,检查singleton非空,直接使用,就会出错
所如果是JDK1.5以及之后的版本,加上Volatile关键字:
private volatile static Singleton singleton;
这样就可以保证singleton对象每次都是从主内存中读取。
静态内部类单例模式:
public classSingleton{ privateSingleton{} public staticSingleton getInstance(){ returnSingletonHolder.sInstance; } /*
静态内部类 */
private static classSingletonHolder{ private static final Singleton sInstance = newSingleton(); } }
优点:只有第一次调用getInstance才会导致sInstance被初始化,第一次调用时候会导致虚拟机加载SingletonHolder类,
这样能保证线程安全,也能保证单例对象唯一,还能延迟单例实例化
枚举单例:
public enumSingletonEnum{ INSTANCE; }
优点:线程安全,唯一,还能防止反序列化。
破坏单例对象的唯一:
1.反射
2.序列化与反序列化