单例写法:
1:饿汉单例模式
优点:在类加载的时候就初始化,避免多线程造成产生多个实例的问题
缺点:不确定在是不是使用的情况就初始化,浪费空间,如果大量使用这个单例写法,浪费空间巨大
2:懒汉单例模式
2.1: 简单懒加载
优点:用户使用时才创建,避免内存浪费
缺点:在多线程情况下回产生多个实例
2.2:同步方法
优点:用户使用时才创建,在多线程场景依然可以保持一个实例
缺点:锁的粒度大,如果初始化实例时需要花费的时间长,容易导致死锁的情况
2.3:DoubleCheck
优点:用户使用时才创建,在多线程场景依然可以保持一个实例且锁的粒度降低,可以避免死锁情况
缺点:内部依然使用synchronized悲观锁,需要牺牲一部分性能
3:静态内部类单例模式
优点:巧妙运用JVM底层类加载顺序解决多个实例的问题,不依赖synchronize锁,性能好
缺点:无法避免反射和序列化的攻击
4:注册式单例模式
4.1:枚举
优点:不依赖synchronize锁,性能好,天然避免了反射攻击和序列化攻击
缺点:类加载就产生了实例,跟饿汉单例模式会浪费空间
4.2:容器
优点:通过容器的方式管理所有的单例对象
缺点:无法避免反射攻击
5:ThreadLocal单例
优点:类加载就初始化当前线程的实例对象,不需要synchronize锁就可以规避多线程问题
缺点:跟着类加载就创建,浪费空间,伪单例,只能满足当前的线程的单例,跨线程不满足
破坏单例
反射攻击:通过Class的api可以无视私有的构造器,绕过限制去创建实例
规避方法:在私有的构造器方法加入判断代码,如果当前有实例存在就抛出异常
序列化攻击:通过对象输出流蒋当前获取到的单例对象输出文件中,然后通过对象输入流构建新的对象
规避方法:在单例类中加resolve方法,返回值为当前对象实例即可
枚举单例模式获得了Class底层api和ObjectInputStream的read api的buffer加持,天然对反射和序列化攻击免疫