缓存框架所要解决的基本问题

缓存框架所要解决的基本问题
缓存框架所要解决的基本问题

缓存框架所要解决的基本问题

Cache 框架,顾名思义即数据的暂存场所和管理。那么该系统的两个核心功能就开始上演了,即存放数据的一个容器和管理缓存容器。就像数据库服务器软件一样,他有一个和新的数据存放场所,同时还有一个管理数据的管理系统。在这个管理系统中就出现了一系列的事务处理,比如说添加,删除,更新以及刷新,删除不成功时如何操作等等。那么该缓存框架需要完成的基本任务就是如何来实例化这么一个数据存储的仓库以及如何有效的来管理仓库中的这些数据,除了上述的这些操作外,还有当仓库中的数据当容量值是再次放入一个缓存实体时该如何有效的淘汰已存在的缓存实体以及该缓存仓库中的数据是否持久有效还是分区管理等等。那么又该如何实例化这么一个缓存仓库呢?首先要考虑的第一问题就是采用何种数据结构来高效的存储这些数据?

1、如何来管理缓存中的对象?----OScache中是通过AbstractConcurrentReadCache和Cache强强联手来管理的

2、如何来管理内存容量的问题?

3、给缓存中添加一个新的缓存实体时,需要注意哪些问题?

当向缓存中添加一个实体时,应该完成哪些必要的业务逻辑呢?我们可以把缓存看做是一个组件,添加一个新的实体是促发该组件的一个事件,该组件能够对该事件进行捕捉并能够进行相应的处理。

4、如何有效的来淘汰缓冲池中对象?

5、如何来管理缓存实体的刷新问题?

实体的更新状态信息具体分应该分为以下几种:分别是还没更新、处于更新状态中、更新完成、更新取消。其中用一个变量来保存当前的更新状态,也即跟踪每一个缓存缓存对象当前处于一种什么状态。同时用什么来表明一个缓存实体过期的标识呢?这里有两种方案:其一就是线程拥有的数量,当线程拥有的数量变为0时,就从缓存容器中释放。其二就是设置一个TTL 发呆的最大

时间。就像是Seesion来管理他内部存储的对象一样。如果再多长时间内没有访问,则及时清除缓存中的一部分对象。这里应该分两种情况来管理缓存中的实例。不然就是去了缓存的真实作用。如果实时性比较高的数据,比如说股票等,那就给这些缓存实体设置一个TTL时间,如果不是实时性数据,那么就将该值设置为-1,比如用户信息这一类的数据。那么如何来判断该缓存实体是否需要刷新呢(OsCache中的CacheEntry的needsRefresh方法)?

(1)当该缓存实体从来都没有更新时,则更新;

(2)如果该实体之前已经刷新过了,则需要刷新

(3)如果传入的参数为0,那么说明该缓存须立即刷新,则刷新;

(4)如果实体刷新策略对象()不为空

(5)当缓存实体的生命周期时间到期了(用设置的刷新期加上最后一次访问的时间距现在的时长为标准,如果小于当前的时长,则说明已经过期了,则需要刷新,否则不需要),

6、如何检测缓存中的数据和数据库中的数据同步与不同步的问题?

7、如何实现磁盘缓存?

磁盘缓存该在什么时候实现呢?这里有两种方案:

(1)当基于内存缓存时容器满时,被淘汰的缓存实体该如何来处理,是直接被jvm的垃圾回收机制回收还是将他们暂持久化到本地?如果是暂时的持久化到本地,那么这些实体又该什么时候置换进内存呢?当内存中未命中时此时应该到外存去查找是否存在。因此系统也应该保留一个缓存文件的集合,因此到时就可以直接在这集合中判断外存是否具有该实体的本地持久化,若有则表示外存命中,则此时应该根据具体的替换算法将内存缓存的实体进行置对换出。从而完成一次内外存一次数据的对换。注意是在当内存未命中时外存命中的情况下进行对换。

(2)基于数据库驱动的网站可以是先将一部分的数据读取出来,这些数据通常是固定式在短时间内不会发生很大变动的数据。这些数据就可以在缓存系统启动的时候通过配置文件的方式配置好。系统就可以在后台悄悄的将这些数据提取出来进行优化处理,下次系统需要取出数据时就可以直接从本地持久化文件中进行提出或者直接从内存中,这将会大大提高系统的响应速度,同时也减少了数据库并发链接的负载。从而给那些实时性较高的数据同数据库的连接提供了比较宽松的机会。如果该部分数据量较大,比如达到上千万级别的java

对象,这时如果jvm的内存配置的较小的话能可能很快就会出现内存溢出的现象,那么又该如何解决该问题?其中的方法就是给出缓存实体容器的大小,这样一来,既不失缓存的最初设想,加入这部分数据一下子全部取出那么在处理时既会带来相应异常的产生,同时也是眉毛胡子一把抓,就没有区分度。如果设置实体缓存的大小,我们强烈建议数据库健表示给出一个计数,即该行数据被选中的次数。那么每当系统启动时就可以有区分度的将这部分数据进行事先的提取。如果数据库建表时没有添加该字段信心,那么缓存系统应该设计自学习算法。该自学习算法需要完成以下基本的任务:

Ⅰ、此时持久化到本地的实例已经不是原生态的java实例,即刚刚从数据库中提取出来的实例。系统对这些实例进行了一次升级,我们称之为对象的升级。该升级需要完成以下几部分内容,给每一个对象添加一个命中的计数,同时添加在同一阶段在命中次数相同的情况每次命中的时间间隔,以备当内外存置换时当命中的次数相同时来更好的体现该缓存实体在时间局部性上更占有优势。

Ⅱ、这些对象该以何种数据结构来存储呢?HashMap?HashSet?Or TreeSet或者其他的数据结构。要使用何种数据结构必须对每种数据结构的特性要了解的相当清楚才能更好熟练的加以运用。这里简单的介绍一下常用集中集合的特点:

ArrayList:内部的实现方式是基于数组的,无容量的限制。但是有个默认的初始容量。因此在插入元素时可能要扩展此时可能会降低系统的性能,但在删除元素时并不会减少数组的容量,但系统也提供了一个后门可以让程序员来维护该容量的大小,即trimTosize方法。当内存紧缺时可以使用该方法来优化内存的使用效率。当要从中查找某个特定的元素时或者判断该集合中是否包含某个特定的元素时底层的实现是通过equals方法来实现的。因此当系统打算用ArrayList集合来存储一个java实例时,可考虑是否要重写父类的equals方法;若要向集合中进行数据的插入或者移除,将会是比较耗时的,但是基于角标的查找其事件复杂度为O(1);并且有时非线程安全的。

LinkedList:基于双向链表机制的实现,元素的插入和移动较快,当是查找的事件复杂度为O(n),当查找某个特定的元素时也是基于equals方法来实现,同时该集合的内部操作也都是非线程安全的,这就需要我们程序员自己来维护多线层访问的安全性。

鉴于OScache的实现,他的实现是每一个缓存实体都对应了一个缓存文件,该缓存文件的扩展名是.oscahe。而该缓存的文件名是基于一种不冲突的算法来实现。文件准备好了,那么当系统要缓存一个实体到本地文件中时,首先通过该缓存的key得到缓存的文件,然后使用ObjectOutputStream流的writeObject 方法来将一个实体写入到磁盘。这里使用装饰设计模式通过BufferedOutputStream流来包装一个FileOutputStream来提高文件的读写操作,实现文件和流的关联方式如以下java代码所示:

FileOutputStream fileout = new FileOutputStream(new

File("src/user.cache"));

BufferedOutputStream out = new

BfferedOutputStream(fileout);

ObjectOutputStream oout = new ObjectOutputStream(bout);

需要持久化的缓存实体通过oout.writeObject(cacheEntry);代码就能实现java对象持久化到本地功能。

那么当一个java对象持久化到本地时,有几个问题需要关注。

(1)该数据是否具备实时性?即和数据库中数据是否保持同步的关系?

(2)另外当需要用该对象时,如何又能组成java对象呢?

8、如何使JSP页面的缓存?

9、实现一个高性能的缓存容器,核心问题就是如何高效的来管理缓存中的这些对象。包括如何实现合理有效的淘汰算法。

10、在java中如何自定义事件以及事件监听器?---java 中基于事件监听的编程模型:

事件监听模型的三大要素:

A:事件源.能够接受外部事件的源体,也即事件产生的地方(单击鼠标:单击是事件,而能接受该事件的源体是鼠标)。基于该原理,当我们在java编程中,对于java的某个实体中的那个方法比较感兴趣,就可以通过事件监听的方法来对他进行跟踪完成系统一些必要的事务处理。比如当向数据库中添加一条数据

或者陆陆续续系统产生一些比较多的数据时,通过事件监听的方法,就可以在后台开启一些线程对数据进行分析然后向主线程中返回一些分析后的结果。B:监听器。表示能够接收接收事件源通知的对象。这表示当事件源接收到一个事件时他不需要自己临时处理,而是让别人早已准备好方法来处理---监听器。这就是为什么要在每一个事件源身上注册相应的监听器。监听器必须注册一个事件源,才能接收这个事件。这个过程是自动的。监听器必须实现接收和处理这个事件的方法。

C:事件处理的方法。用于处理事件的对象,事件源产生一个事件并把这个事件发送到一个或多个监听程序,监听程序只是等待这个事件的产生并处理,然后将结果返回。也叫事件处理程序。

为了实现基于事件监听驱动的编程模型。就要预先定义好该系统中最关心感兴趣的那些个事件。这些事件用EventType类封装,该类中封装了最基本的几个事件,分别为实体添加,实体更新,实体刷新,实体移除,实体过期以及实体重构。该类的UML图如下图所示:

事件类型有了,就该定义相关的事件监听器了。在java中所有对相关事件感兴趣并想加以监听的这一类对象都必须实现EventLister接口。查看该接口中可发现没有一个方法。在java中若发现一个接口中没有一个方法此时仅仅表现的是标记的作用。因此jdk的设计者们也强烈建议若要实现某一类事件的监听都最好实现该接口。这里的实现访java swing 中的事件监听器的简单实现。只给出一个actionPerformed方法。每一个事件的产生都要表明该事件的事件类型以便于开发人员可以更好的具体事件具体处理。因此在该方法的形参中绑定了一个触发该事件类型的实例。当发生该事件时,就可以实例化该事件类型的一个实例并指明当前的事件类型。具体的java代码如下所示:

cacheEntry.getListener().actionPerformed(new

EventType(EventType.ENTRY_ADD));

这是当向缓存容器中添加一个实体时所触发的事件监听。

事件处理的代码就是在具体实现了事件监听接口的子类实现。当上面的cacheEntry.getListener()方法执行时,就可以返回该缓存实体上所注册的具体事件监听器。actionPerformed方法的执行(也即actionPerformed 消息的触发)也使具体的事件处理代码得到执行。因此我们就可以在实现事件监听的子类中专注实现相应的业务逻辑。而相应的添加其他操作就可以在系统内部自动的完成,比如说事件的注册及销毁等。事件监听的编程模型时序图如下图所示:

11、java 中的volatile关键字是什么意思?他表示含义是什么呢?在什么情况下才使用他呢?

Volatile :易变的,不稳定的。他是在多线程编程的基础下保证变量的安全性。

相关主题
相关文档
最新文档