单例模式

1、饿汉式

/**
 * Comments: 饿汉式单例,项目启动就创建,执行效率高,
 * 不管用不用都先创建,所以比较消耗系统资源(内存浪费)
 *
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 16:10
 */
public class HungrySingleton {

    private static final HungrySingleton hungrySingleton;

    /**
     * 隐藏构造方法,防止外部创建实例
     */
    private HungrySingleton() {}

    static {
        hungrySingleton = new HungrySingleton();
    }

    public HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

2.1 懒汉式-加锁双重判断

/**
 * Comments: 懒汉式单例,延迟创建,需要的时候才创建,效率降低了
 * 加锁了,所以性能降低了
 *
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 16:15
 */
public class LazySingleton {

    /** 加上volatile是防止指令重排序 导致线程混乱 */
    private volatile static LazySingleton lazySingleton;

    /**
     * 隐藏构造方法,防止外部创建实例
     */
    private LazySingleton() {}

    /**
     * 优化后的代码,双重检查,减小加锁导致的性能降低
     *
     * @return LazySingleton
     */
    public static LazySingleton getInstance() {
        // 第一次检查是否需要阻塞
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                // 第二次检查是否需要创建对象
                if (lazySingleton == null) {
                    // 上面声明lazySingleton的时候已经分配地址,
                    // 这里需要重新指向新的地址,防止指令重排序导致的
                    // 线程混乱,lazySingleton属性加上volatile关键字
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}

2.2 懒汉式-内部静态类写法

/**
 * Comments: 懒汉式静态内部类的写法
 * 利用了java本身语法的特点,性能高,避免了内存浪费
 * 类文件加载是xxxx.class字节码的形式,内部静态类是xxxx.$LazyInstance .class的形式
 * 只有在初始化 类的时候才会加载静态类,所以是懒汉式
 *
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 17:03
 */
public class LazyStaticInnerClassSingleton {

    private LazyStaticInnerClassSingleton() {
        // 防止反射 破坏单例
        if (LazyInstance.INSTANCE != null) {
            throw new RuntimeException("不允许非法创建");
        }
    }

    public LazyStaticInnerClassSingleton getInstance() {
        return LazyInstance.INSTANCE;
    }

    private static class LazyInstance {
        private final static LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
    }
}

反射破坏单例

/**
 * Comments: 反射测试
 *
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 17:13
 */
public class ReferTest {

    public static void main(String[] args) {
        try {
            Class<?> clazz = LazyStaticInnerClassSingleton.class;
            Constructor<?> constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);

            Object instance = constructor.newInstance();
            System.out.println(instance);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

单例是会被反射破坏的,除了将构造方法私有化之外还需要在构造方法里面加判断,初始化了就不能再创建。但是枚举在jdk底层 防止了反射,所以枚举是线程安全的。

3、枚举式

/**
 * Comments: 注册(枚举)式单例
 * 因为枚举类底层限制了通过反射创建枚举类,所以反射无法破坏枚举式单例
 *
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 17:42
 */
public enum  EnumSingleton {

    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Object getInstance() {
        return INSTANCE;
    }
}

4、容器注册式-spring里面的ioc就是这样

/**
 * Comments: 注册式单例,spring中就是用这种容器管理Bean
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 20:44
 */
public class ContainerSingleton {

    private ContainerSingleton() {}

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className) {
        synchronized (ioc) {
            Object instance = null;
            if (!ioc.containsKey(className)) {
                try {
                    instance = Class.forName(className).newInstance();
                    ioc.put(className, instance);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return instance;
            } else {
                return ioc.get(className);
            }
        }
    }
}

除了反射可以破坏单例,序列化与反序列化也可以破坏单例,如下例子:

5.1用饿汉式单例举例

/**
 * Comments: 序列化与反序列化
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 21:15
 */
public class SerializableSingleton implements Serializable {

    private static final SerializableSingleton hungrySingleton;

    /**
     * 隐藏构造方法,防止外部创建实例
     */
    private SerializableSingleton() {}

    static {
        hungrySingleton = new SerializableSingleton();
    }

    public static SerializableSingleton getInstance() {
        return hungrySingleton;
    }
}

5.2 - 测试类

/**
 * Comments: TODO
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 21:14
 */
public class SerializableSinglertonTest {
    public static void main(String[] args) {

        SerializableSingleton s1 = null;
        SerializableSingleton s2 = SerializableSingleton.getInstance();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();
            
            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SerializableSingleton)ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

序列化与反序列化破坏单例的解决方法:加个方法readResolve()

/**
 * Comments: 序列化与反序列化
 * ProjectName: parent
 *
 * @author camel
 * @date 2020/9/13 21:15
 */
public class SerializableSingleton implements Serializable {

    private static final SerializableSingleton hungrySingleton;

    /**
     * 隐藏构造方法,防止外部创建实例
     */
    private SerializableSingleton() {}

    static {
        hungrySingleton = new SerializableSingleton();
    }

    public static SerializableSingleton getInstance() {
        return hungrySingleton;
    }

    /**
     * 防止反序列化破坏单例
     * 
     * @return Object
     */
    public Object readResolve() {
        return hungrySingleton;
    }
}

6 - ThreadLocal方式

public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值