Spring中常见的设计模式——单例模式
一、单例模式的应用场景
单例模式(singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。J2EE中的ServletContext,ServletContextConfig等;Spring中的ApplicationContext、数据库连接池等。
二、饿汉式单例模式
饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象。它是绝对的线程安全、在线程还没出现以前就实现了,不可能存在访问安全问题。
优点:没有增加任何锁,执行效率高,用户体验比懒汉式好。
缺点:类加载的时候就初始化了,用不用都进行,浪费内存。
Spring 中IoC容器ApplocationContext本身就是典型的饿汉式单例模式:
- public class HungrySingleton {
- private static final HungrySingleton h = new HungrySingleton();
- private HungrySingleton() {
- }
- public static HungrySingleton getInstance() {
- return h;
- }
- }
饿汉式单例模式适用于单例对象较少的情况。
三、懒汉式单例模式
被外部调用才会加载:
- public class LazySimpleSingleton {
- private LazySimpleSingleton() {
- }
- private static LazySimpleSingleton lazy = null;
- public static LazySimpleSingleton getInstance() {
- if (lazy == null) {
- lazy = new LazySimpleSingleton();
- }
- return lazy;
- }
- }
利用线程创建实例:
- public class ExectorThread implements Runnable {
- @Override
- public void run() {
- LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
- System.out.println(Thread.currentThread().getName() + ":" + simpleSingleton);
- }
- }
客户端代码:
- public class LazySimpleSingletonTest {
- 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");
- }
- }
结果:
- END
- Thread-1:singleton.Lazy.LazySimpleSingleton@298c37fd
- Thread-0:singleton.Lazy.LazySimpleSingleton@6ebc1cfd
可以看到 产生的两个实例的内存地址不同说明产生了两个实例,大家可以通过以下打断点的方式实现不同Thread运行状态见进行切换。
要解决线程问题第一反应是加 synchronized 加在创建实例的地方:public static synchronized LazySimpleSingleton getInstance(),但当线程数量较多时,用Synchronized加锁,会使大量线程阻塞,就需要更好的解决办法:
- public static LazySimpleSingleton getInstance() {
- if (lazy == null) {
- synchronized (LazySimpleSingleton.class) {
- if (lazy == null) {
- lazy = new LazySimpleSingleton();
- }
- }
- }
- return lazy;
- }
synchronized (lock) lock这个对象就是 “锁”,当两个并行的线程a,b,当a先进入同步块,即a先拿到lock对象,这时候a就相当于用一把锁把synchronized里面的代码锁住了,现在只有a才能执行这块代码,而b就只能等待a用完了lock对象锁之后才能进入同步块。但是用到 synchronized 总归是要上锁的,对性能还是有影响,那就用这种方式:用内部类的方式进行懒加载。
- public class LazyInnerClassSingleton {
- private LazyInnerClassSingleton() {
- }
- private static final LazyInnerClassSingleton getIngestance() {
- return LazyHolder.LAZY;
- }
- private static class LazyHolder {
- private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
- }
- }
内部类在LazyInnerClassSingleton类加载时加载,解决了饿汉式的性能问题,LazyInnerClassSingleton在内部类加载时,getIngestance()方法被调用之前实例化,解决了线程不安全问题。
四、反射破坏单例
- public class LazyInnerClassSingletonTest {
- public static void main(String[] args) {
- try {
- Class<?> clazz = LazyInnerClassSingleton.class;
- //通过反射回去私有构造方法
- Constructor constructor = clazz.getDeclaredConstructor(null);
- //强制访问
- constructor.setAccessible(true);
- //暴力初始化
- Object o1 = constructor.newInstance();
- //创建两个实例
- Object o2 = constructor.newInstance();
- System.out.println("o1:" + o1);
- System.out.println("o2:" + o2);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
结果:
- o1:singleton.Lazy.LazyInnerClassSingleton@1b6d3586
- o2:singleton.Lazy.LazyInnerClassSingleton@4554617c
创建了两个实例,违反了单例,现在在构造方法中做一些限制,使得多次重复创建时,抛出异常:
- private LazyInnerClassSingleton() {
- if (LazyHolder.class != null) {
- throw new RuntimeException("不允许创建多个实例");
- }
- }
这应该就是最好的单例了,哈哈哈。
五、注册式单例模式
注册式单例模式又称为登记式单例模式,就是将每个实例都登记到某个地方,使用唯一标识获取实例。注册式单例模式有两种:枚举式单例模式、容器式单例模式。注册式单例模式主要解决通过反序列化破坏单例模式的情况。
1.枚举式单例模式
- public enum EnumSingleton {
- INSTANCE;
- private Object data;
- public Object getData() {
- return data;
- }
- public void steData(Object data) {
- this.data = data;
- }
- public static EnumSingleton getInstance() {
- return INSTANCE;
- }
- }
测试代码:
- public class EnumSingletonTest {
- public static void main(String[] args) {
- try {
- EnumSingleton instance1 = EnumSingleton.getInstance();
- EnumSingleton instance2 = null;
- instance1.steData(new Object());
- FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(instance1);
- oos.flush();
- oos.close();
- FileInputStream fis = new FileInputStream("EnumSingleton.obj");
- ObjectInputStream ois = new ObjectInputStream(fis);
- instance2 = (EnumSingleton) ois.readObject();
- ois.close();
- System.out.println(instance1.getData());
- System.out.println(instance2.getData());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
结果:
- java.lang.Object@568db2f2
- java.lang.Object@568db2f2
那枚举式单例是如何解决反序列化得问题呢?
通过反编译,可以在EnumSingleton.jad文件中发现static{} 代码块,枚举式单例模式在静态代码块中给INSTANCE进行了赋值,是饿汉式单例模式的实现。查看JDK源码可知,枚举类型其实通过类名和类对象找到一个唯一的枚举对象。因此,枚举对象不可能被类加载器加载多次。
当你试图用反射破坏单例时,会报 Cannot reflectively create enum objects ,即不能用反射来创建枚举类型。进入Customer的newInstance(),其中有判断:如果修饰符是Modifier.ENUM,则直接抛出异常。JDK枚举的语法特殊性及反射也为美剧保驾护航,让枚举式单例模式成为一种比较优雅的实现。
2.容器式单例
- public class ContainerSingleton {
- private ContainerSingleton() {
- }
- private static Map<String, Object> ioc = new ConcurrentHashMap<>();
- public static Object getBean(String className) {
- synchronized (ioc) {
- if (!ioc.containsKey(className)) {
- Object o = null;
- try {
- o = Class.forName(className).newInstance();
- ioc.put(className, o);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return o;
- } else {
- return ioc.get(className);
- }
- }
- }
- }
spring中使用的就是容器式单例模式。
Spring中常见的设计模式——单例模式的更多相关文章
- Spring中常见的设计模式——代理模式
一.代理模式的应用场景 生活中的中介,黄牛,等一系列帮助甲方做事的行为,都是代理模式的体现.代理模式(Proxy Pattern)是指为题对象提供一种代理,以控制对这个对象的访问.代理对象在客户端和目 ...
- Spring中常见的设计模式——适配器模式
一.适配器模式的应用场景 适配器模式(Adapter Pattern)是指将一个类的接口转换成用户期待的另一个接口,使原本接口不兼容的类可以一起工作,属于构造设计模式. 适配器适用于以下几种业务场景: ...
- Spring中常见的设计模式——委派模式
一.委派模式的定义及应用场景 委派模式(Delegate Pattern)的基本作用是负责任务的调用和分配,跟代理模式很像,可以看做特殊情况下的静态的全权代理,但是代理模式注重过程,而委派模式注重结果 ...
- iOS中常见的设计模式——单例模式\委托模式\观察者模式\MVC模式
一.单例模式 1. 什么是单例模式? 在iOS应用的生命周期中,某个类只有一个实例. 2. 单例模式解决了什么问题? 想象一下,如果我们要读取文件配置信息,那么每次要读取,我们就要创建一个文件实例,然 ...
- Spring中常见的设计模式——策略模式
策略模式(Strategy Pattern) 一.策略模式的应用场景 策略模式的应用场景如下: 系统中有很多类,而他们的区别仅仅在于行为不同. 一个系统需要动态的在集中算法中选择一种 二.用策略模式实 ...
- Spring中常见的设计模式——工厂模式
一.简单工厂模式 简单工厂模式(Simple Factory Pattern)由一个工厂对象决定创建哪一种产品类的实例,简单工厂模式适用于工厂类负责创建对象较少的情况,且客户端只需要传入工厂类的参数, ...
- Spring中常见的设计模式——原型模式
1.原型模式应用场景 当遇到大量耗费劳动力的 get,set赋值场景时,如下: public class SetGetParam { public void setParam(UserDto user ...
- Spring中常见的设计模式——模板模式
一.模板模式的应用场景 模板模式又叫模板方法模式(Template Method Pattern),指定义一个算法的骨架,并允许自雷为一个或者多个步骤提供实现.模板模式使得子类可以在不改变算法结果的情 ...
- 设计模式:JDK和Spring中常见的设计模式
设计模式 总结 类 工厂模式 封装创建过程,只对结果负责 BeanFactory.Calender 单例模式 全局唯一 ApplicationContext.Calender 原型模式 多重影分身之术 ...
随机推荐
- day27作业
FTP需求 客户端可以注册登录 client:输入一个login sever:执行login 客户端登陆后可以选择文件夹上传与下载 client:输入一个upload,download sever:执 ...
- Java基础(三十三)JDBC(3)操作数据库
一.添加数据 在SQL语句中,一条INSERT语句只能添加一条记录,因此分为几种情况进行添加数据操作. 1.添加一条记录 (1)如果只需要添加一条记录,通常情况下通过Statament实例完成. tr ...
- Unity事件系统
# 1.前言Unity中事件/委托有着广泛的应用,本文通过封装一个简易的事件的系统,来统一管理消息的传递.此功能在简易应用或者事件较少的体现不出太好的作用,但是对于事件应用较多时,可以减少脚本之间的耦 ...
- Java自动化测试框架-06 - 来给你的测试报告化个妆整个形 - (下)(详细教程)
简介 经过上一次的化妆和整形,有客户提出需求能不能将那个普通会员的套餐再升级一下,再漂亮一点.所以这次咱们就来看看从哪里下刀可以使它变得再漂亮一点点. 上一篇文章修改了一些基本的ReportNG信息, ...
- Scrapy 框架入门简介
一.Scrapy框架简介 Scrapy 是用 Python 实现的一个为了爬取网站数据.提取结构性数据而编写的应用框架. Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中. ...
- flask插件之flask_session会话机制
flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,该组件则将支持session保存到多个地方,如: redis:保存数据的一 ...
- CSPS模拟 76
前序遍历,中序遍历,后序遍历 说的都是根节点在前,根节点在中,根节点在后. 长记性!
- VM安装CentOS系统
本篇文章主要介绍了VMware安装Centos7超详细过程(图文) 1.软硬件准备 软件:推荐使用VMwear,我用的是VMwear 12 镜像:CentOS7 ,如果没有镜像可以在官网下载 :htt ...
- MySQL传统点位复制在线转为GTID模式复制
1. GTID优缺点 MySQL传统点位复制在5.7版本前是主要的主从复制模式,而随着MySQL5.6版本引入GTID,并且MySQL5.7进行各方面的优化以后,在mySQL5.7(尤其是MySQL ...
- Uva 227-Puzzle 解题报告
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...