spring5 核心原理-学习 -单例模式

内容都是从Tom老师的书中摘抄,记录下来方便自己学习。
单例模式(singleton pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例是创建型模式。
一、饿汉式单例模式是在类加载时就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没有出现前就实例化了,不可能存在访问安全问题。饿汉式单例模式适用于单例对象较少的情况。

public class HungrySingleton {
    private  static  final  HungrySingleton hungrySingleton=new HungrySingleton();
    private  HungrySingleton(){}
    public  static  HungrySingleton getInstance(){
        return  hungrySingleton;
    }
}

另一种写法

public class HungrySingleton {

    private static final HungrySingleton hungrySingleton;

    static {
        hungrySingleton = new HungrySingleton();
    }

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

二、懒汉式单例模式是指被外部类调用的时候内部类才会加载。

/*
*此种方式存在线程安全问题
*/
public class LazySimpleSingleton {
    private  LazySimpleSingleton(){}
    private  static LazySimpleSingleton lazySimpleSingleton=null;
    public  static  LazySimpleSingleton getInstance(){
        if (lazySimpleSingleton == null) {
            lazySimpleSingleton=new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}
public class ExectorThread implements Runnable {

    public void run() {
        LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + ":" + simpleSingleton);
    }
}
public class LazySimpleSignletonTest {
    public static void main(String[] args) {

            Thread t1 = new Thread(new ExectorThread());
            Thread t2 = new Thread(new ExectorThread());
            t1.start();
            t2.start();

        System.out.println("end");
    }
}

加入断点,右键断点,选中Thread模式
加入断点,右键断点,选中Thread模式

不断切换线程,观察单例对象的内存状态,发现单例对象被实例化两次,后面的实例覆盖了前面的实例,我们看到的是一个假象,认为线程是安全的
在这里插入图片描述
在这里插入图片描述

/*
*加入synchroized 关键字
*/
public class LazySimpleSingleton {
    private  LazySimpleSingleton(){}
    private  static LazySimpleSingleton lazySimpleSingleton=null;

    public  synchronized static  LazySimpleSingleton getInstance(){
        if (lazySimpleSingleton == null) {
            lazySimpleSingleton=new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}

通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞,直到第一个线程执行完
兼顾线程安全又能提升程序性能的写法

/*
*
*/
public class LazySimpleSingleton {
    private  LazySimpleSingleton(){}
    private volatile static LazySimpleSingleton lazySimpleSingleton=null;
    public  static  LazySimpleSingleton getInstance(){
        if (lazySimpleSingleton == null) {
            synchronized (LazySimpleSingleton.class){
                if (lazySimpleSingleton == null) {
                    lazySimpleSingleton=new LazySimpleSingleton();
                }
            }
        }
        return lazySimpleSingleton;
    }
}

通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞。此时,阻塞并不是基于整个LazySimpleSingleton类,而实在getInstance()方法内部,只要逻辑不太复杂,对于调用者而言感知不到。(此处没有测试性能差距,留个位置,测试后补充)

不使用synchronized关键字的写法

/*
*  采用静态内部类的写法,这种方式兼顾了饿汉式单例模式的内存浪费问题和synchronized的性能问题,内部类一定是在方法掉用之前初始化,巧妙避免了线程安全问题。  
*/
public class LazyInnerClassSingleton {
    private LazyInnerClassSingleton(){}
    public static  final  LazyInnerClassSingleton getInstance(){
        return  LazyHolder.LAZY_INNER_CLASS_SINGLETON;
    }
    private static class  LazyHolder{
        private  static final  LazyInnerClassSingleton LAZY_INNER_CLASS_SINGLETON=new LazyInnerClassSingleton();
    }
}
/*
*  反射破坏单例  ,代码是随意写的,主要为了测试用
*/
public class LazyInnerClassSingletonTest {
    public static void main(String[] args) {
        Class<?> clazz=LazyInnerClassSingleton.class;
        Constructor<?> constructor= null;
        try {
            constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            try {
                Object o1=constructor.newInstance();
                Object o2=constructor.newInstance();
                System.out.println(o1==o2);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
/*
*  序列化破坏单例
*/
public class Seriablesingleton implements Serializable {
    private   final  static Seriablesingleton INSTANCE=new Seriablesingleton();
    private  Seriablesingleton(){}
    public  static Seriablesingleton getInstance(){
        return  INSTANCE;
    }
//    private Object readResolve(){
//        return  INSTANCE;
//    }
}
/*
*  将上述代码注释部分取消注释即可
* 虽然增加readResolve()方法返回实例解决了单例模式被破坏问题,但实际上实例化了两次,只是新创建的实例没有被返回。
* 如果创建对象的动作发生频率加快,就意味着内存分配开销也会随之增大
*/
public class SeriablesingletonTest {
    public static void main(String[] args) {
        Seriablesingleton s1=null;
        Seriablesingleton s2=Seriablesingleton.getInstance();
        FileOutputStream fos=null;
        try {
            fos=new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.close();
            FileInputStream fis=new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois=new ObjectInputStream(fis);
            s1= (Seriablesingleton) ois.readObject();
            ois.close();
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1==s2);

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值