oscache 的问题

一:现象:

系统页面无法打开,数据显示异常

二:原因分析:(代码分析,比较枯燥,请耐心查看)

 

在使用oscache的时候,为了减轻开发者的负担,同时也让代码具有更好的可读性,我们一般会使用annotation的方式来完成缓存的设值和取值

Code1

 

在调用当前方法的时候 会调用annotation拦截器的方法进行拦截,然后将返回值设置到缓存系统并返回。调研系统annotation拦截器调用的方法为CacheInterceptor.java doCache 方法

Code:2

 oscache 的问题_第1张图片

问题出现在第二步和第三部的连贯上,在oscache中,EntryUpdateStatecache entry当前所处状态的表示,oscache尽量避免了使用synchronize,引入了许多状态参量。状态变迁图示如下:

oscache 的问题_第2张图片

假设在A线程调用getCache的时候,缓存条目不存在,或者过期,oscache会抛出一个异常,也就是臭名昭著的NeedsRefreshException,当这个异常出现,相应的EntryUpdateState的状态为updating(也就是更新中),此时如果B线程再来调用,就会block住,代码如下:

oscache 的问题_第3张图片

一个do  while循环。只有当A 调用 putInCache EntryUpdateState的状态变为complete,同时会调用updateState.notifyAll() 方法解除wait动作)或者cancelUpdate(状态此时的装变为cancelled,同时调用updateState.notify() 方法解除wait动作)方法才会打破这个循环。

getCachedObject中如果抛出这个异常,会怎么样呢?看代码:

Code4

oscache 的问题_第4张图片

从代码中,可以看到处理逻辑是捕捉这个异常,并将cachedObject设置为空,并返回。

 

因此就看第三步了,代码如下:

Code5

oscache 的问题_第5张图片

上面code4代码逻辑如下:

一:如果缓存返回值为空,则调用本地方法进行执行

二:如果执行结果为空,调用 cancelUpdate 方法将状态更新

三:如果执行结果不为空,则调用putInCache 方法将结果放入到缓存中

四:返回结果

请大家闭上眼睛思考下,上面一段代码

 

正常情况下,这段代码不会有问题,但是如果此时本地方法调用出现异常,会怎么样呢?也就是说pjp.proceed();这段代码抛出异常,此时当前A线程就永远不会更新状态,并进行notify操作,那么此时B线程就会一直在循环中,并堵塞以后所有的线程操作,最终结果就是操作会越来越缓慢,应用死掉。

解决方法其实也很简单,就是捕捉应用抛出的异常

Code6

oscache 的问题_第6张图片

后记:一路分析,抛出这个NeedsRefreshException 倒没有什么问题,关键还是那个状态问题,那么就要思考这个状态一定要么?如果不要可以么?如果可以的话,那么oscache当时为什么要这个状态呢?

 

 

你可能感兴趣的:(OScache)