精讲单例设计模式与单例防御

什么是单例

保证一个虚拟机中的类中只有一个实例

加载类型

立即加载:项目启动就加载
懒加载:需要使用到的时候才加载

static修饰的变量或代码块为项目启动就加载,且只会加载一次

单例的七种模式

饿汉式 项目启动就立即加载导致项目启动慢

/**
 * 饿汉式
 * 优点:线程安全
 * 缺点:项目加载将实例化单例,占用内存资源,项目加载慢
 * @author administrator
 */
public class UnLazySingleton {
    private UnLazySingleton() {
    }

    private static UnLazySingleton unLazySingleton = new UnLazySingleton();

    public static UnLazySingleton getSingleton(){
        return unLazySingleton;
    }

    public static void main(String[] args) {
        System.out.println(UnLazySingleton.getSingleton());
        System.out.println(UnLazySingleton.getSingleton());
    }
}

懒汉式 线程不安全 :创建对象包含创建(写入内存)和赋值(读取内存对象),获取对象是线程安全的只涉及读操作
一个线程是否安全依据–要么读要么写

/**
 * 懒汉式
 * 优点:需要使用再加载
 * 缺点:线程不安全
 *
 * @author administrator
 */
public class LazySingleton {
    public LazySingleton() {
    }

    private static LazySingleton lazySingleton = null;

    public static LazySingleton getSingleton() {

        if (lazySingleton == null) {
            //模拟多线程出现线程安全问题
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
//        System.out.println(LazySingleton.getSingleton());
//        System.out.println(LazySingleton.getSingleton());
        //模拟多线程出现线程安全问题,需要注释上面代码
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(LazySingleton.getSingleton());
                }
            }).start();
        }
    }
}

懒汉式 线程安全 多线程时效率不高,因获取对象只涉及读操作,线程是安全的,没必要加锁

/**
 * 懒汉式
 * 优点:需要使用再加载
 * 缺点:线程安全,多线程时效率低,因为在创建对象和获取对象的时候都加了锁,获取对象的时候可以不加锁
 *
 * @author administrator
 */
public class LazySafeSingleton {
    private LazySafeSingleton() {
    }

    private static LazySafeSingleton lazySingleton = null;

    public synchronized static LazySafeSingleton getSingleton() {

        if (lazySingleton == null) {
            lazySingleton = new LazySafeSingleton();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        System.out.println(LazySingleton.getSingleton());
        System.out.println(LazySingleton.getSingleton());
    }
}

DCL双检查锁 线程安全 多线程时初始化对象效率低,获取对象效率高,因初始化时加了锁

/**
 * double check lock 双检查锁 解决线程安全和效率问题,但是第一次加载慢
 *
 * @author administrator
 */
public class DCLSingleton {
    private DCLSingleton() {
    }

    //volatile保证可见性和指令重排
    private volatile static DCLSingleton lazySingleton = null;

    public static DCLSingleton getSingleton() {
        if (lazySingleton == null) {
            //创建对象包含创建(写入内存)和赋值(读取内存对象)非原子操作存在线程安全,需要加锁  (原子操作:要么读要么写)
            synchronized (DCLSingleton.class) {
                //防止多线程重复创建,导致为非单例对象
                if (lazySingleton == null) {
                    lazySingleton = new DCLSingleton();
                }
            }
        }
        //返回只包含读操作,线程是安全的,没必要加锁
        return lazySingleton;
    }

    public static void main(String[] args) {
        System.out.println(LazySingleton.getSingleton());
        System.out.println(LazySingleton.getSingleton());
    }
}

内部类,线程安全效率高 但是存在反射和序列化问题

/**
 * 内部类 解决DCL第一次加载效率问题,但是存在被反射和序列化的问题
 *
 * @author administrator
 */
public class InnerSingleton {
    private InnerSingleton() {
        System.out.println("初始化构造方法");
    }

    public static InnerSingleton getSingleton() {
        return InnerClass.innerSingleton;
    }

    private static class InnerClass {
        //只初始化一次(static修饰的),保证线程安全和效率
        private final static InnerSingleton innerSingleton = new InnerSingleton();
    }

    public static void main(String[] args) {
        System.out.println(InnerSingleton.getSingleton());
        System.out.println(InnerSingleton.getSingleton());

    }
}

枚举 线程安全,效率高,解决反射和序列化 不能反射和序列化原因

public enum EnumSingleton {
    INSTANCE;

    // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    public void add() {
        System.out.println("add方法...");
    }

    public static void main(String[] args) throws Exception {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

容器管理

/**
 * 使用容器管理单例
 *
 * @author administrator
 */
public class ContainManagerSingleton {
    private static Map objMap = new ConcurrentHashMap<>();

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        {
            return objMap.get(key);
        }
    }
}

使用反射攻击单例

/**
 * 反射攻击单例
 *
 * @author administrator
 */
public class AttackByReflectSingleton {
    private AttackByReflectSingleton() throws Exception {
        //解决反射破解单例
        if (attackByReflectSingleton != null) {
            throw new Exception("报错了");
        }
    }

    private static AttackByReflectSingleton attackByReflectSingleton = null;

    public static AttackByReflectSingleton getSingleton() throws Exception {
        if (attackByReflectSingleton == null) {
            attackByReflectSingleton = new AttackByReflectSingleton();
        }
        return attackByReflectSingleton;
    }

    public static void main(String[] args) throws Exception {
        AttackByReflectSingleton singleton = AttackByReflectSingleton.getSingleton();
        //使用反射破解单例模式,模拟攻击将解决方案注释掉就行
        Constructor constructor = AttackByReflectSingleton.class.getDeclaredConstructor();
        //设置可访问私有化方法
        constructor.setAccessible(true);
        AttackByReflectSingleton attackByReflectSingleton = constructor.newInstance();
        //获取到不同对象表示被破解了
        System.out.println(singleton);
        System.out.println(attackByReflectSingleton);


    }
}

通过序列化攻击单例

/**
 * 序列化攻击单例
 */
public class AttackBySerializableSingleton implements Serializable {
    private AttackBySerializableSingleton() {
    }
    //序列化:内存到硬盘
    //反序列化:硬盘到内存
    private static AttackBySerializableSingleton attackBySerializableSingleton = new AttackBySerializableSingleton();

    public static AttackBySerializableSingleton getSingleton() {
        return attackBySerializableSingleton;
    }

    public static void main(String[] args) throws Exception {
        AttackBySerializableSingleton singleton = AttackBySerializableSingleton.getSingleton();
        FileOutputStream fos = new FileOutputStream("F:\\code\\singleton.Object");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(singleton);
        oos.close();
        fos.close();
        FileInputStream fis = new FileInputStream("F:\\code\\singleton.Object");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object singleton2 = ois.readObject();
        ois.close();
        fis.close();
        System.out.println(singleton);
        System.out.println(singleton2);
    }
    //返回序列化获取对象 ,保证为单例,解决序列化破解单例
    public Object readResolve() {
        return attackBySerializableSingleton;
    }
}

思想来源

你可能感兴趣的:(设计模式系列)