单例模式
来源:互联网 发布:mac系统安装steam 编辑:程序博客网 时间:2024/06/08 13:24
https://mp.weixin.qq.com/s/lZDqy5WatnORY9fEqjjztw
懒汉式
懒加载:需要时才去创建实例
非线程安全
/** * 因为在需要的时候才创建吧, 显得很“懒”<br> * 先执行newInstance,再执行构造方法<br> * 非线性安全<br> * @author MrLeeYongSheng * */public class LazyMan { private static LazyMan instance; private LazyMan() {} public static LazyMan newInstance() { if(instance==null) instance=new LazyMan(); return instance; }}
线程安全
/** * @author MrLeeYongSheng * 当有多个线程同时执行newInstance()时<br> * 若有一个线程获取SynchronizedLazyMan.class线程锁则其他线程需要等待该线程释放锁 */public class SynchronizedLazyMan { private static SynchronizedLazyMan instance; private SynchronizedLazyMan() {} public static synchronized SynchronizedLazyMan newInstance() { if(instance==null) instance=new SynchronizedLazyMan(); return instance; }}
双重锁线程安全
/** * @author MrLeeYongSheng * 第一道锁:<br> * 若有一个线程获取SynchronizedLazyMan.class线程锁则其他线程需要等待该线程释放锁<br> * 第二道锁:<br> * volatile的可见性,有序性保证了主内存的引用变量正确写入后才允许读取的操作<br> * <code>instance=new DoubleLockLazyMan();</code><br> * 该操作JVM分三步执行:<br> * 1)向堆内存分配空间<br> * 2)初始化对象<br> * 3)将引用指向内存地址<br> * 由于编译优化时会执行"指令重排序"<br> * 执行顺序有可能:<br> * 1-2-3<br> * 1-3-2<br> * 若以后者的顺序执行,有可能在对象还没初始化完毕的时候,此时instance!=null,被别的线程获取到了不正确的对象<br> */public class DoubleLockLazyMan { private static volatile DoubleLockLazyMan instance; private DoubleLockLazyMan() {} public static DoubleLockLazyMan newInstance() { if(instance==null) { synchronized(DoubleLockLazyMan.class) { if(instance==null) instance=new DoubleLockLazyMan(); } } return instance; }}
饿汉式
因为一用该类,在加载类时就创建实例,极度饥渴
/** * @author MrLeeYongSheng * 由于太饥饿, 一上来就创建了对象<br> * 先执行构造方法再执行getInstance<br> * 线程安全的<br> */public class HungryMan { private static final HungryMan INSTANCE = new HungryMan(); private HungryMan(){} public HungryMan getInstance() { return INSTANCE; }}
懒饿汉式
实现懒加载的饿汉式
/** * @author MrLeeYongSheng * 利用了JVM的特性<br> * 在使用LazyHungryManHelper之前并不会加载该类<br> * 在调用 getInstance()时,在加载LazyHungryManHelper的时候对INSTANCE实例化,实现了懒加载<br> */public class LazyHungryMan { private LazyHungryMan(){} private static final class LazyHungryManHelper{ private static final LazyHungryMan INSTANCE = new LazyHungryMan(); } public LazyHungryMan getInstance() { return LazyHungryManHelper.INSTANCE; } public void test() { System.out.println("111"); }}
黑客攻击
反射
攻击
public class ReflectMan { public static void main(String[] args) throws Exception { /*//反射获取 Class<?> clazz = LazyHungryMan.class; Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); LazyHungryMan newInstance = (LazyHungryMan) declaredConstructor.newInstance(); newInstance.test();*/ /*//攻击添加flag保护的单例类,抛异常 PreventReflectMan.newInstance().test(); Class<?> clazz = PreventReflectMan.class; Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); PreventReflectMan newInstance = (PreventReflectMan) declaredConstructor.newInstance(); newInstance.test();*/ //击破flag保护 PreventReflectMan man = PreventReflectMan.newInstance(); man.test(); Class<?> clazz = PreventReflectMan.class; Field declaredField = clazz.getDeclaredField("flag"); declaredField.setAccessible(true); declaredField.setBoolean(man, false); Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); PreventReflectMan newInstance = (PreventReflectMan) declaredConstructor.newInstance(); newInstance.test(); }}
反击反射
public class PreventReflectMan { private static boolean flag = false; private static PreventReflectMan instance; private PreventReflectMan() throws Exception { if(flag==true) { throw new Exception("哈哈哈"); } } public static PreventReflectMan newInstance() throws Exception{ if(flag==false && instance==null) { instance = new PreventReflectMan(); flag = true; } return instance; } public void test() { System.out.println("222"); }}
总结:抵不住反射攻击
反序列
攻击
public class SerializableMan { public static void main(String[] args) throws Exception { File file = new File("D:/a.data"); ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(file)); oo.writeObject(PreventSerializableMan.newInstance()); oo.close(); ObjectInputStream oi = new ObjectInputStream(new FileInputStream(file)); PreventSerializableMan man = (PreventSerializableMan) oi.readObject(); oi.close(); man.test();//抛空指针异常 }}
防御
public class PreventSerializableMan implements Serializable{ private static final long serialVersionUID = -4231160544787653273L; private static boolean flag = false; private static PreventSerializableMan instance; private PreventSerializableMan() throws Exception { if(flag==true) { throw new Exception("哈哈哈"); } } public static PreventSerializableMan newInstance() throws Exception{ if(flag==false && instance==null) { instance = new PreventSerializableMan(); flag = true; } return instance; } public void test() { System.out.println("222"); } /** * 在反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中 * 又包含readResolve()方法的话,会直接调用readResolve()方法来获取实例<br> * 通过返回一个null从而使其反序列化失败 * @return * @throws Exception */ private Object readResolve() throws Exception { return null; }}
总结抵住了攻击
终极单例
/** * @author MrLeeYongSheng * 别看是枚举类型,但实际上反编译可知枚举实际上就是一个继承Enum的类。<br> * 所以枚举类型的本质还是一个类<br> * 因为枚举的特点,你只会有一个实例,同时保证了线程安全、反射安全和反序列化安全<br> *<h1>神一样的存在</h1> */public enum TerminatorEnum { INSTANCE; public void test() { System.out.println("厉害啊,一个顶十个"); }}
附加
了解volatile
http://s4.uczzd.cn/webapp/webview/article/news.html?app=smds-iflow&aid=8195357621751254675&cid=0&zzd_from=smds-iflow&uc_param_str=dndseiwifrvesvntgipf&rd_type=share&pagetype=share&btifl=100&sdkdeep=2&sdksid=3f52e685-7c91-6bb9-01c3-316d0b5256d5&sdkoriginal=3f52e685-7c91-6bb9-01c3-316d0b5256d5
阅读全文
0 0
- 单例、单例模式
- 单例模式-多线程单例模式
- 单件模式(单例模式)
- 设计模式------单例模式
- 设计模式------单例模式
- 设计模式-单例模式
- 设计模式 - 单例模式
- 设计模式---单例模式
- 设计模式---单例模式
- PHP模式-单例模式
- 【设计模式】单例模式
- 设计模式-单例模式
- 设计模式----单例模式
- 设计模式--单例模式
- 设计模式-单例模式
- 单例模式(单子模式)
- 设计模式-单例模式
- [设计模式] 单例模式
- 程序设计竞赛题集
- 《inside Bluetooth low energy》note_01
- mybatis复杂一对多映射配置示例
- Swift函数式编程之Map&Reduce&Filter
- matlab的fft
- 单例模式
- Jmeter FTP服务
- 在raspberry上安装skimage
- Eclipse +MinGW编译和使用Box2D开源软件
- 侧滑加传值
- Android零基础入门第60节:日历视图CalendarView和定时器Chronometer
- hbase命令 (总结1)
- XListView 的运用步骤
- 自动化爬取开开贷借贷黑名单(python&selenium)