面试宝典之单例模式详解

定义:确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,构造方法私有化
优点:减少了内存开销,可以避免对资源的多重占用,设置全局访问点,严格控制访问
缺点:没有接口,扩展比较难,要扩展的话只有修改代码
常见写法:饿汉单例,懒汉单例,注册式单例又叫容器式单例
饿汉单例(初始化创建)

private static final LazyStaticInnerClassSingleton aa=new LazyStaticInnerClassSingleton();
private LazyStaticInnerClassSingleton (){}
public static LazyStaticInnerClassSingleton getInstance(){
    return aa;
}

优点:执行效率高,性能高,没有任何的锁
缺点:在某些情况下可能会造成内存浪费
解决办法懒汉式单例(外部类调用时才创建)

public class LazySimpleSingletion {
    private static LazySimpleSingletion instance;
    private LazySimpleSingletion(){};
    public  static  LazySimpleSingletion getInstance(){
        if (instance == null){
            instance =new LazySimpleSingletion();
        }
        return instance;
    }
}

优点:节省了内存
缺点:线程不安全(比如两个线程同时访问到,就会有两种结果,如果是同一个实例的话分
两种情况一种是正常顺序执行,一种是后者覆盖前者,如果不是同一个实例都是按顺序返回)
解决办法:加一个synchronized关键字,加了之后第一个线程没有执行完的,第二个线程会进入阻塞状态等待第一个线程执行完成。

public synchronized static LazySimpleSingletion getInstance(){
        if (instance == null){
             instance =new LazySimpleSingletion();
        }
        return instance;
    }

缺点:性能低,等待时间太长,比如地铁站门口等待人数过多
解决办法:用方法内部类,和if判断是否有阻塞

public static  LazySimpleSingletion getInstance(){
        synchronized (LazySimpleSingletion.class){
            if (instance == null){//这样其实还是阻塞并没有解决性能问题
                instance =new LazySimpleSingletion();
            }
        }
        return instance;
    }

这个和上一次区别在于一个在地铁外面排队一个在地铁里面排队,虽然说在里面排队要好听一些但是问题还是在排队!

public static  LazySimpleSingletion getInstance(){
		if (instance == null){//放在这里就解决了这个问题,检查是否有阻塞
        	synchronized (LazySimpleSingletion.class){
            	if (instance == null){//这样其实还是阻塞并没有解决性能问题,检查是否要重新创建实例
                instance =new LazySimpleSingletion();
            	}
        	}
		}
        return instance;
    }

优点:性能提高
缺点:程序的可读性难度加大,不够优雅,这种方法会有指令排序的问题
指令重排序的问题是指(变量里面的值要指向new出来的对象时会有一个先后问题,一但出现问题就会造成线程紊乱
解决办法:加volatile关键字比如:(private volatile static LazySimpleSingletion instance;
缺点:不够优雅
解决办法:

private LazyStaticInnerClassSingleton(){};
    private static LazyStaticInnerClassSingleton getInstance(){
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder{
    private static final LazyStaticInnerClassSingleton INSTANCE =new LazyStaticInnerClassSingleton();
}

优点:优雅,利用了java反射,性能高,内存不浪费
缺点:能够被反射破坏
解决办法:

//修改无参构造进行判断
    private LazyStaticInnerClassSingleton(){
        if (LazyHolder.INSTANCE!=null){
            throw new RuntimeException("不允许非法访问");
        }
    };

缺点:虽然解决了反射破坏的可能但是不够优雅
解决办法:枚举式单例

public enum  EnumSingleton {
    INSTANCEOF;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCEOF;
    }
}

缺点:会内存浪费,不适合大批量使用
优点:不会被破坏是因为枚举的底层不支持被反射拿到数据
解决办法:注册式单例——将所有实例都缓存到统一的容器中,使用标示进行获取

private static Map<String,Object> ioc=new ConcurrentHashMap<>();
    public static Object getInstance(String className){
        if(!ioc.containsKey(className)){
            Object instance= null;
            try {
                //如果不存在就存到map里面
                instance = Class.forName(className).newInstance();//newInstance是java反射时创建对象的方法
                ioc.put(className,instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return  instance;
        }else {
            //如果存在就直接返回
            return ioc.get(className);
        }
}

优点:解决了大批量的问题
缺点:线程不安全
如果遇到序列化破坏单例怎么办?
解决办法:增加方法 private Object readResolve(){return INSTANCE}//桥接模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值