单例模式,保证全局只有一个实例,分为以下几种方式实现:
1.饿汉式,缺点,创建出来后不管用不用,内存里面都占用空间:
public class Single {
private static Single single= new Single();
private Single() {
}
public static Single getInstance(){
return single;
}
}
2.懒汉式,缺点是每次都要判断,且需要同步,有一定的时间花费:
public class Single {
private static Single single;
private Single() {
}
public static synchronized Single getInstance(){
if(single==null){
single = new Single();
}
return single;
}
}
3.双重加锁机制,被volatile
修饰的变量的值,JVM会强制将线程本地内存的变量刷新到共享主内存,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,否则两个线程同时获取对象,A创建了对象,但很可能出现对象还没刷新到主内存的情况,B去判断的时候还是空的,又去创建了一个对象。且它能禁止指令重排序优化,从而确保多个线程能正确的处理该变量。考虑一种情况,在多线程下,使用下面这种方式来获取对象,未重排序的对象创建顺序是:分配内存地址→初始化实例对象→将对象指向分配的内存地址,如果重排序导致后面两步反过来,线程A执行了1、3,还没执行第2步,线程B通过下面的方法去拿对象判断到不为空,直接返了一个未初始化的对象,这时候用这个对象来执行操作就会出错:
public class Single {
private static volatile Single single;
private Single() {
}
public static Single getInstance(){
if(single==null){
synchronized (Single.class) {
if (single == null) {
single = new Single();
}
}
}
return single;
}
}
4.类级内部类,不使用就不会加载:
public class Single {
private Single() {
}
public static class SingleInner{
private static Single single = new Single();
}
public static Single getInstance(){
return SingleInner.single;
}
}
5.枚举,如果对内存泄漏有一定要求的话,尽量不要使用这种方式。
6.为防止通过反射调用私有构造器生成对象,判断对象不为空的时候抛出异常。
7.要防止序列化反序列化拿到新的对象,要添加一个readResolve
方法。
网友评论