设计模式--单例模式--三种实现方法---和单例模式的安全隐患

1.饥饿单例模式的实现:

优点: 饥饿单例,不用使用锁,使用的效率高,同时没有线程安全问题的困扰
缺点: 类加载的时候就要初始化,导致程序启动初始化慢,同时可能后面用不到,但却一直 占着内存,造成浪费
/**
 * @author : GONG ZHI QIANG
 * @data : 2019-08-31 , 14:27
 * @user : SnaChat
 * @project: DesignPattern
 * @description :实现--饥饿单例模式
 */
 
public class HungrySingleton {

    private static final HungrySingleton singleton = new HungrySingleton();

    private HungrySingleton() {
    }

    private HungrySingleton getInstance() {
        return singleton;
    }
}

2. 懒汉单例模式

缺点:需要注意到线程安全的问题,可以使用synchronized, 或者ReentrantLock等方式实现线程同步问题
/**
 * @author : GONG ZHI QIANG
 * @data : 2019-08-31 , 14:33
 * @user : SnaChat
 * @project: DesignPattern
 * @description : 懒汉单例模式
 */
public class LazySingleton {

    private static final Object lock = new Object();

    private LazySingleton() {
    }

    private static LazySingleton singleton = null;

    /**
     * 此处的方法可能出现线程安全的问题,使用 synchronized实现 加锁,但是这样在高并发情况下,又将会出现性能问题,下面将给出一个更优化的方案
     * @return
     */
    public static LazySingleton getInstance() {
        synchronized (lock) {
            //这里有一个IDEA快捷键 只有输入 lazy 就有代码块
            if (singleton == null) {
                singleton = new LazySingleton();
            }
            return singleton;
        }
    }
}

3.使用静态内部类 实现单例模式

  • 使用静态内部类实现单例模式,当我们不使用单例的时候,单例对象不会创建
  • 同时,内部类是在方法调用之前一定要实现初始化,所以这里规避了多线程的不安全访问的问题
  • 使用静态内部类的方式同时规避了前两种实现方式的弊端,
/**
 * @author : GONG ZHI QIANG
 * @data : 2019-08-31 , 14:44
 * @user : SnaChat
 * @project: DesignPattern
 * @description :
 */
public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton(){}

    public static final StaticInnerClassSingleton getInstance(){
        return StaticInnerClass.SINGLETON;
    }

    private static class StaticInnerClass{
        private static final StaticInnerClassSingleton SINGLETON = new StaticInnerClassSingleton();
    }
}

4.使用反射的方式—破坏单例模式

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //使用反射的方式,创建单例的对象
        Class<?> calzz = StaticInnerClassSingleton.class;
        Constructor constructor = null;
        constructor = calzz.getDeclaredConstructor(null);
        constructor.setAccessible(true);

        StaticInnerClassSingleton first = (StaticInnerClassSingleton) constructor.newInstance();
        StaticInnerClassSingleton second = (StaticInnerClassSingleton) constructor.newInstance();
        
		//此处的输出结果返回的是false,证明单例类实际上创建了两个对象,这不是我们想看到的结果
        System.out.println("(second == first) ? =  " + (second == first)); 
    }
下面是解决的方案
	private StaticInnerClassSingleton(){
        if (StaticInnerClass.SINGLETON != null) {
            throw new RuntimeException("创建的对象已经存在,不能再次创建爱对象!");
        }
    }

在这里插入图片描述
上面的方式保证了我们使用反射的方式创建单例,也是安全的。
但是,这样就安全了吗? NO!

5.使用序列化的方式–也可能会破坏单例模式

这种情况下,从磁盘中读取出来的单例类,创建对象,可能在内存中存在多个

6、更好的单例模式—使用容器创建(Spring 框架管理Bean)

public class ContainerSingleton {
    private ContainerSingleton() {
      
    }
    private static Map<String, Object> container = new ConcurrentHashMap<>();

    public static Object getBean(String beanName) {
        synchronized (container) {
            if (!container.containsKey(beanName)) {
                Object newObject = null;
                try {
                    newObject = Class.forName(beanName).newInstance();
                    container.put(beanName, newObject);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return newObject;
            } else {
                return container.get(beanName);
            }
        }
    }
}

上面的例子中的容器实现的单例不是线程安全的

7.每个线程都有一个副本 ,不论每个线程调用调用多少次,都会返回同一个对象,这个就能够保证线程的安全性,这是一种一空间换取时间的方式

public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal<ThreadLocalSingleton>(){
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };
    public ThreadLocalSingleton() {
    }
    public static ThreadLocalSingleton getInstance(){
        return threadLocal.get();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值