单例模式

来源:互联网 发布: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

原创粉丝点击