剑指架构师系列-struts2的缓存

     2022-03-22     339

关键词:

 

Struts2的缓存中最重要的两个类就是ReferenceMap与ReferenceCache。下面来解释下ReferenceCache中的get()方法。

public V get(final Object key) {
		V value = super.get(key);
		return (value == null) ? internalCreate((K) key) : value;
}

通过key来获取value操作首先调用了super.get(key)方法,也就是调用了ReferenceMap中变量delegate的get()方法,这个变量的定义如下:

transient ConcurrentMap<Object, Object> delegate;  

支持并发的容量,所以不会有线程的问题。但是这个value值很可能为null,为什么呢?

(1)这个值本来就没有,当然为null

(2)key或value值存储的类型为软引用或弱引用,当JVM内存吃紧或回收内存时很可能让key/value对中的某个值为null,如果某个值为null,这个key/value对就会失效,会被Deman线程清理出ConcurrentMap容器中。  

如果为空,那么就走internalCreate()函数:

V internalCreate(K key) {
		try {
			FutureTask<V> futureTask = new FutureTask<V>(new CallableCreate(key));   // 1

			// use a reference so we get the same equality semantics.
			Object keyReference = referenceKey(key);   // 2
			// 有就返回futureTask,没有就返回null
			Future<V> future = futures.putIfAbsent(keyReference, futureTask); // 3
			if (future == null) {
				// winning thread.
				try {
					if (localFuture.get() != null) {  // 4
						// 不允许在同一个缓存内嵌套创建
						throw new IllegalStateException("Nested creations within the same cache are not allowed.");
					}
					
					localFuture.set(futureTask); // 5
					
					futureTask.run();            // 6
					V value = futureTask.get();  // 7
					putStrategy().execute(this, keyReference,referenceValue(keyReference, value));  // 8
					return value;
				} finally {
					localFuture.remove();    // 9
					futures.remove(keyReference); // 10
				}
			} else {
				// wait for winning thread.
				return future.get();
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
	}

  

(1)首先创建一个FutureTask对象,我们随后介绍。

(2)对key进行了一次封装引用(强引用、弱引用或者软引用),为什么要封装呢?主要还是想对弱引用与软引用的值进行比较。有机会我们再展开讨论这个问题。

(3)putIfAbsent()方法是个原子操作,也就是不存在就放入。futures变量定义如下:

transient ConcurrentMap<Object, Future<V>> futures = new ConcurrentHashMap<Object, Future<V>>();

当future为空时,进入了步骤4

(4)这里有个localFuture变量,定义如下:

transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();

这是个ThredLocal变量,本来就是线程安全的,但是localFuture.get()不为null时表示了嵌套创建的异常,这是什么原因呢?大家看第5步时为localFuture设置了值,而9处又移除了这个值。如果不为空,那说明线程设置了值在运行9之前的代码时又重新进入了这个if判断中。

 

到底是什么操作让这个线程又重要进入了if判断中呢?接着往下看。

(6)这里运行了FutureTask任务,也就是CallableCreate类的call()方法,然后线程在7处阻塞等待结果。另外一个线程运行call()方法来生成结果。

class CallableCreate implements Callable<V> {
		K key;
		public CallableCreate(K key) {
			this.key = key;
		}
		public V call() {
			// try one more time (a previous future could have come and gone.)
			// 这里又对值进行了一次判定  从缓存中查找值
			V value = internalGet(key);  
			if (value != null) {
				return value;
			}
			// create value.
			value = create(key); // 自定义加载数据的方式
			if (value == null) {
				throw new NullPointerException("create(K) returned null for: " + key);
			}
			return value;
		}
	}

在call()方法中再次调用了intrnalGet(key)来判断缓存中是否有这个值,我们记得在之前的线程中也做过这个判断,就是调用get()方法中的super.get()方法的时候。为什么还要重新判断一下呢?

 

现在假设有C线程在A线程执行if判断语句中的代码时也用相同的key调用了get()方法,但是A线程已经得到B线程的结果,执行完了10处理的代码,而这里的C线程恰好已经走过了get()方法中的super.get()方法,

导致没有获取到结果,在走if判断后调用了futureTask来重新获取结果,这时的call()方法中的internalGet()判断可就起作用了,直接取出B线程运行好的被A线程放入Map中。

 

还有一种情况,如果C线程在A线程还没有运行得到结果(在7处阻塞着呢)那么就会走else中的代码,因为A线程已经将futureTask放到Map中了。C线程也被要求阻塞等待B线程的结果。

 

如果缓存中没有则只能调用create()方法创建了,在ReferenceCache类中create()方法定义如下:

	protected abstract V create(K key);

这是一个抽像方法,Struts2中有一个具体的使用来看一下:

final Map<Class<?>, List<Injector>> injectors = new ReferenceCache<Class<?>, List<Injector>>() {
		
		protected List<Injector> create(Class<?> key) {
			List<Injector> injectors = new ArrayList<Injector>();
			addInjectors(key, injectors);
			return injectors;
		}
		
};

可以看到覆写了create()方法,所以call()方法就会调用上面的create()方法来创建值。 

 

现在来想想4处的嵌套创建代码,如果这里的create()调用了get()方法,并且传入相同的key值,会出现什么结果呢?

 

剑指架构师系列-innodb存储引擎spring事务与缓存

 事务与锁是不同的。事务具有ACID属性:原子性:持久性:由redolog重做日志来保证事务的原子性和持久性,一致性:undolog用来保证事务的一致性隔离性:一个事务在操作过程中看到了其他事务的结果,如幻读。锁是用于解决隔离... 查看详情

剑指架构师系列-activemq队列的使用

 安装ActiveMQ只需要下载包后解压,然后就可以启动与关闭ActiveMQ了,如下: ./activemqstart./activemqstop访问管理页面:http://10.10.20.20:8161/admin用户名和密码默认为:admin/admin spring.activemq.broker-url--指定ActiveMQbroker的URL,默认自... 查看详情

剑指架构师系列-hibernate需要掌握的annotation

 1、一对多的关系配置@Entity@Table(name="t_order")publicclassOrder{ @Id @GeneratedValue privateintid; privateStringname; /**该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,*而且这种... 查看详情

剑指架构师系列-springboot的logback日志记录

  SpringBoot集成了Logback日志系统。Logback的核心对象主要有3个:Logger、Appender、Layout 1、Logback Logger:日志的记录器 主要用于存放日志对象,也可以定义日志类型、级别。级别:ERROR、WARE、INFO、DEBUG和TRACE。没有FATA... 查看详情

剑指架构师系列-设计模式

 1、单例模式:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式有以下几个要素:私有的构造方法指向自己实例的私有静态引用以自己实例为返回值的静态的公有的方法     ... 查看详情

剑指架构师系列-logstash分布式系统的日志监控

 Logstash主要做由三部署组成:Collect:数据输入Enrich:数据加工,如过滤,改写等Transport:数据输出下面来安装一下:wgethttps://download.elastic.co/logstash/logstash/logstash-2.3.2.tar.gztar-zxvflogstash-2.3.2.tar.gz  在logstash-2.3.2目录下创建文 查看详情

剑指架构师系列-持续集成之maven+nexus+jenkins+git+springboot

 1、Nexus与Maven 先说一下这个Maven是什么呢?大家都知道,Java社区发展的非常强大,封装各种功能的Jar包满天飞,那么如何才能方便的引入我们项目,为我所用呢?答案就是Maven,只需要粘贴个Jar包的地址,Maven就会自动到... 查看详情

成为架构师课程系列高性能系统设计之分布式缓存

...和分库分表之后,已经可以支撑十几万DAU了,整体系统的架构也变成了下面这样:从整体上看,数据库分了主库和从库,数据也被切分到多个数据库节点上。但随着并发的增加,存储数据量的增多,数据库的磁盘IO逐渐成了系统... 查看详情

成为架构师课程系列使用cache-aside模式将数据存储在缓存中(usingthecache-asidepatterntostoredatainthecache)

目录 前言背景和问题解决方案问题和注意事项何时使用此模式例子前言按需将数据从数据存储加载到缓存中(Cache-Aside )。这种模式可以提高性能,还有助于保持缓存中保存的数据与底层数据存储中的数据之间的一致性... 查看详情

成为架构师课程系列架构师的核心能力地图

目录架构师核心能力总结#综合技术能力分层总结#数据结构和算法知识图谱总结#Java工程师【核心基础】知识图谱总结 查看详情

成为架构师课程系列怎样进行概念架构(conceptualarchitecture)?

目录 前言什么是概念架构概念架构阶段的3个步骤 初步设计高层分割分层式概念服 查看详情

成为架构师课程系列预备架构pre-architecture的故事

 目录前言Pre-architecture的故事Pre-architecture核心“四步法”需求结构化架构约束 查看详情

架构师之路系列文章

目录文章目录目录企业数字化转型架构师之路业务架构应用架构API经济数据库设计设计模式系统架构分布式系统RPC远程调用分布式消息队列分布式任务队列微服务架构ServiceComb部署架构高可靠、高可用、高并发、高性能、高可扩... 查看详情

架构师成长系列|云原生时代的devops之道

...|郝树伟(花名:流生)??阿里云高级研发工程师本文整理自架构师成长系列2月17日直播课程。关注“阿里巴巴云原生”公众号,回复?“217”,即可获取对应直播回放链接及PPT下载链接。导读:DevOps是一种软件开发人员和IT人员之间... 查看详情

成为架构师课程系列架构分层:我们为什么一定要这么做?

目录#什么是分层架构#分层有什么好处#如何来做系统分层#分层架构的不足#课程小结#思考时间#拓展阅读在系统从0到1的阶段,为了让系统快速上线,我们通 查看详情

阿里架构师详解缓存架构:如何减少不必要的计算?

...的计算压力。互联网应用的核心解决思路就是采用分布式架构,提供更多的服务器,从而提供更多的计算资源,以应对高并发带来的计算压力及资源消耗。那么有没有办法减少到达服务器的并发请求压力呢?或者请求到达服务器... 查看详情

4大系列33课时,距离你成为架构师还差这一套课程

今天,「架构师成长系列直播」正式上线啦!疫情爆发,为了避免大家在线下聚集,我们决定延期举办众多线下Meetup。我们迅速召集了包括ApacheDubboPMC、ApacheRocketMQ中国社区发起人&PMC、SpringCloudAlibabaPMC等30+位阿里技术专家,以... 查看详情

阿里架构师吐血整理:从源码到架构的spring全系列笔记,已全部分享

...码笔记SpringBoot核心笔记springcloudalibaba笔记SpringCloud微服务架构核心笔记第一份:Spring高级源码笔记Spring概述核心思想手写实现IoC和AOPSpringIOC应用SpringIOC源码深度剖析SpringAOP应用SpringAOP源码深度剖析Spring全系列资 查看详情