spring5 核心原理-学习 -单例模式
2020-04-20 15:05
435 查看
内容都是从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模式
不断切换线程,观察单例对象的内存状态,发现单例对象被实例化两次,后面的实例覆盖了前面的实例,我们看到的是一个假象,认为线程是安全的
/* *加入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
- 收藏
- 分享
- 文章举报
相关文章推荐
- Spring5-核心原理-学习 委派模式
- Spring5-核心原理-学习 代理模式
- spring5 核心原理-学习 -原型模式
- Spring5-核心原理-学习 各种设计模式之间的比对
- Spring5-核心原理-学习 装饰者模式
- Spring5-核心原理-学习 适配器模式
- 学习笔记3:《大型网站技术架构 核心原理与案例分析》之 大型网站架构模式
- java核心知识点学习----equals和==的比较、单例模式,饿汉式,饱汉式
- 【《大型网站技术架构-核心原理与案例分析》学习笔记】大型网站架构演化(一)
- 逆向工程核心原理学习笔记(十四):栈帧1
- 学习笔记2:《大型网站技术架构 核心原理与案例分析》之 大型网站架构演化
- 逆向工程核心原理学习笔记(十四):栈帧1
- 学习笔记4:《大型网站技术架构 核心原理与案例分析》之 大型网站核心架构要素
- 《大型网站核心架构+核心原理与案例分析》之网站架构模式
- 架构师---(大型网站技术架构核心原理与案例分析)2网站架构模式
- J2EE核心模式的学习
- 【Spring】IOC核心源码学习(三):bean标签和自定义标签实现原理
- 逆向工程核心原理学习笔记2-基址重定位基本原理
- 【每周一本书】之《深入浅出强化学习:原理入门》:零起点入门掌握AlphaGo的核心强化学习算法
- Struts2核心工作原理学习