`

Ehcache(08)——可阻塞的Cache——BlockingCache

 
阅读更多

可阻塞的CacheBlockingCache

 

       在上一节我们提到了显示使用Ehcache锁的问题,其实我们还可以隐式的来使用Ehcache的锁,那就是通过BlockingCacheBlockingCacheEhcache的一个封装类,可以让我们对Ehcache进行并发操作。其内部的锁机制是使用的net.sf.ehcache.concurrent.ReadWriteLockSync,与显示锁调用是一样的实现,而ReadWriteLockSync内部使用的是Javajava.util.concurrent.locks.ReadWriteLock

       BlockingCache拥有两个构造函数,它们都接收一个Ehcache对象,其中一个还接收一个指定并发数量的参数numberOfStripes,另一个没有numberOfStripes参数,但其将使用默认值,默认值为2048numberOfStripes的值必须大于0,且为2的指数。接收的参数cache表示真正进行操作的Ehcache对象,BlockingCache只是对其进行了封装,使其支持并发操作。

 

Java代码   收藏代码
  1. public BlockingCache(final Ehcache cache, int numberOfStripes) throws CacheException {  
  2.     super(cache);  
  3.     this.stripes = numberOfStripes;  
  4.     this.cacheLockProviderReference = new AtomicReference<CacheLockProvider>();  
  5. }  
  6.   
  7. public BlockingCache(final Ehcache cache) throws CacheException {  
  8.     this(cache, StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);  
  9. }  

  

       虽然我们的Ehcache是支持锁调用的,但BlockingCache与显示锁调用不同的是它不是直接通过所持有的Ehcache来获取锁和释放锁的,而是由其内部完成的。在从BlockingCacheget元素时,是支持并发读的,这没有问题,但如果对应key对应的元素不存在,则线程将被阻塞,直到调用了对应的put()方法存放了一个对应的key的元素为止。这是怎么做到的呢?我们来看一下BlockingCache的源码。

Java代码   收藏代码
  1. public Element get(final Object key) throws RuntimeException, LockTimeoutException {  
  2.     getObserver.begin();  
  3.     Sync lock = getLockForKey(key);  
  4.     acquiredLockForKey(key, lock, LockType.READ);  
  5.     Element element;  
  6.     try {  
  7.         element = underlyingCache.get(key);  
  8.     } finally {  
  9.         lock.unlock(LockType.READ);  
  10.     }  
  11.     if (element == null) {  
  12.         acquiredLockForKey(key, lock, LockType.WRITE);  
  13.         element = underlyingCache.get(key);  
  14.         if (element != null) {  
  15.             lock.unlock(LockType.WRITE);  
  16.             getObserver.end(GetOutcome.HIT);  
  17.         } else {  
  18.             getObserver.end(GetOutcome.MISS_AND_LOCKED);  
  19.         }  
  20.         return element;  
  21.     } else {  
  22.         getObserver.end(GetOutcome.HIT);  
  23.         return element;  
  24.     }  
  25. }  

 

       上述是BlockingCache的核心get()方法。我们可以看到首先我们获取到了对应keySyncSync是一个接口,其实现类通过持有的Lock对象可以对对应的key进行Read LockWrite Lock。另外有一点需要注意的是对于同一个key而言,我们使用的是同一个Lock对象。通过上一节对Ehcache显示锁介绍,我们知道Read Lock之间是不会阻塞的。所以当我们在试图get一个元素时:

       1、如果对应的key没有Write锁,那我们的代码都可以顺利的执行到判断element是否为null那一行,这个时候获取到的Read锁已经释放,不会影响以后获取其Write锁;

       2、如果对应key存在Write锁,则在调用“acquiredLockForKey(key, lock, LockType.READ);”时就会被阻塞直到对应的Write锁被释放。

       3、如果获取到的element不为null,则将直接返回。

       4、如果elementnull,则将获取对应keyWrite锁,此时如果存在其它线程获取了该keyRead锁,则将阻塞直到不存在对应的Read锁。

       5、获取到Write锁以后重新get一次对应的元素,因为有可能在判断elementnull之后,获取对应的Write锁之前,已经有线程put了一个对应的元素到Cache中。如果获取到了对应的元素则释放对应的Write锁,然后返回获取到的元素。

       6、获取到了Write锁之后其它试图获取对应keyRead锁或Write锁的线程都将被阻塞。如果获取到了Write锁之后对应的元素还是为null,则将直接返回。此时除该线程以外的其它调用了get()方法的线程都将被阻塞,因为当前线程的Write锁还没有释放。

 

       通过上面的代码和分析我们知道,如果在利用BlockingCacheget()方法获取一个元素时,如果对应的元素不存在,则除最终获取到Write锁的线程以外的线程都将被阻塞,而获取到了对应keyWrite锁的线程该如何释放其Write锁呢?这是通过往BlockingCacheput一个对应key的元素来释放的。BlockingCache是实现了Ehcache接口的,所以Ehcache拥有的put*()方法,BlockingCache都有,但是在BlockingCacheput*()方法中都加入了一个doAndReleaseWriteLock的逻辑。我们先来看一个put()方法的实现。

Java代码   收藏代码
  1. public void put(final Element element) {  
  2.   
  3.     doAndReleaseWriteLock(new PutAction<Void>(element) {  
  4.         @Override  
  5.         public Void put() {  
  6.             if (element.getObjectValue() != null) {  
  7.                 underlyingCache.put(element);  
  8.             } else {  
  9.                 underlyingCache.remove(element.getObjectKey());  
  10.             }  
  11.             returnnull;  
  12.         }  
  13.     });  
  14. }  

 

       我们可以看到在该put()方法内部调用了一个doAndReleaseWriteLock()方法,从该方法名以及其接收的参数我们可以看出,doAndReleaseWriteLock()方法的作用就是执行接收的参数PutActionput()方法,然后释放对应keyWrite锁,而且PutAction的构造是接收一个Element参数的,这样在PutAction中的put()方法中我们就可以使用该Element对象了。doAndReleaseWriteLock()方法的实现如下所示。

Java代码   收藏代码
  1. private <V> V doAndReleaseWriteLock(PutAction<V> putAction) {  
  2.   
  3.     if (putAction.element == null) {  
  4.         returnnull;  
  5.     }  
  6.     Object key = putAction.element.getObjectKey();  
  7.     Sync lock = getLockForKey(key);  
  8.     if (!lock.isHeldByCurrentThread(LockType.WRITE)) {  
  9.         lock.lock(LockType.WRITE);  
  10.     }  
  11.     try {  
  12.         return putAction.put();  
  13.     } finally {  
  14.         //Release the writelock here. This will have been acquired in the get, where the element was null  
  15.         lock.unlock(LockType.WRITE);  
  16.     }  
  17. }  

 

       从源代码我们可以看到,其内部实现跟我们设想的差不多。在PutAction所持有的Element不为null的情况下会判断当前线程是否持有对应keyWrite锁,如果没有对应keyWrite锁,则将试图获取其Write锁,这个时候如果该keyWrite锁已经被别的线程获取了,则在这里将进行阻塞。拥有了Write锁之后就可以执行PutAction对象的put()方法了,执行完后就可以释放对应keyWrite锁了。

       回过头来看,之前从BlockingCacheget元素时,如果对应元素不存在,则该线程将获取到对应keyWrite锁(并发情况下,究竟是哪一个线程会获取到该keyWrite锁是不定的),将使其它试图获取该keyWrite锁或Read锁的线程阻塞。如果该线程此时往BlockingCacheput一个对应key的元素,则该线程所持有的Write锁将会释放,其它线程可以顺利的获取该keyRead锁和Write锁,即可以顺利的调用BlockingCacheget()方法获取对应的元素。BlockingCache就是为使用页面缓存而设计的,当多个线程同时请求一个页面时,如果缓存中存在对应的页面,则可以直接返回,Read锁之间不会阻塞;如果对应的页面不存在,那么这个时候只有一个线程会返回null,其它线程都将被阻塞,返回值为null时,Ehcache将会把对应的页面putBlockingCache中,此时该线程所持有的Write锁将释放,而其它被阻塞的线程也将可以顺利的获取到该页面。这样一来就可以避免多个线程在get到的元素为null时,都同时往缓存中put对应的页面,造成不必要的资源浪费。如果有页面缓存这样的需求的话使用BlockingCache是再合适不过了。关于Ehcache使用页面缓存的更多信息将在下一篇博文中介绍。

       刚刚说的BlockingCache就是为页面缓存设计的。如果用户需要自己使用BlockingCache时注意在获取到的元素为null时要释放对应的Write锁。这个时候有两种方法,一是调用BlockingCache的任意put方法,往其中存放一个对应key的元素;二是自己定义一个类继承BlockingCache,然后开放一个释放锁的方法,对应逻辑可以参考BlockingCachedoAndReleaseLock()方法,这是因为其内部获取锁的方法getLockForKey()的访问类型是protected

 

       此外,BlockingCache在获取锁时如果被阻塞了,那么阻塞时间是不定的,它有可能会非常长。如果不希望阻塞时间太长的话,我们可以通过BlockingCachesetTimeoutMillis()方法设置最长阻塞时间,单位为毫秒,这样如果一个线程在timeoutMillis时间内还没有获取到对应的锁则将抛出LockTimeoutException

 

(注:本文是基于Ehcache2.8.1所写)

摘自:http://haohaoxuexi.iteye.com/blog/2119737

分享到:
评论

相关推荐

    cache/ehcache缓存使用

    本文将详细讲解"cache/ehcache缓存使用"的相关知识点,包括缓存的基本概念、Ehcache的介绍、以及如何在Java应用中使用Ehcache进行缓存操作。 首先,我们要理解什么是缓存。缓存是一种存储技术,它临时存储常用或...

    hibernate4+spring4+springmvc+ehcache+自己写的cache系统

    在IT行业中,构建高效、可扩展的Web应用是至关重要的,而"hibernate4+spring4+springmvc+ehcache+自己写的cache系统"是一个常见的技术栈,用于实现这样的目标。这个组合提供了完整的数据持久化、服务层管理、前端...

    Ehcache简单应用——RSSReaderTag

    NULL 博文链接:https://snowolf.iteye.com/blog/728877

    Mybatis-ehcache 1.2.1源码(ehcache-cache-mybatis-ehcache-1.2.1.tar

    2. **Cache 接口实现**:Ehcache 实现了 Mybatis 的 Cache 接口,提供了缓存的生命周期管理和数据存取操作。 3. **插件机制**:Mybatis 使用插件机制来集成 Ehcache,通过拦截器(Interceptor)在 SQL 执行前后进行...

    Mybatis入门实例(二)——添加ehcache缓存支持

    在本篇《Mybatis入门实例(二)——添加ehcache缓存支持》中,我们将深入探讨如何在Mybatis框架中集成Ehcache作为二级缓存,以提高数据访问的效率和性能。Ehcache是一个开源的Java分布式缓存,广泛用于缓存应用程序中...

    Mybatis-ehcache 1.2.1源码(ehcache-cache-mybatis-ehcache-1.2.1.zip)

    Mybatis-ehcache 1.2.1 是一个集成Mybatis和Ehcache的缓存模块,用于提高Mybatis框架的查询效率。Ehcache是一个广泛使用的Java缓存解决方案,它能够有效地存储和检索数据,减少数据库的负载,提高应用程序性能。在...

    本地缓存(一)ehcache/jcs/cache4j/jcs的性能测试与使用场景分析

    本文将主要探讨本地缓存的几个常见实现,包括Ehcache、JCS(Java Caching System)和Cache4j,并进行性能测试,同时分析它们各自适用的使用场景。 首先,Ehcache是一个广泛使用的开源Java缓存解决方案,它提供了...

    Ehcache 简单的监控

    至于“工具”,Ehcache生态系统中有许多辅助工具,如`Ehcache Cache Manager`,它可以帮助我们创建、修改和删除缓存实例;还有`Ehcache CLI`,一个命令行工具,用于执行诸如查看缓存状态、清理缓存等操作。 在`...

    ehcache-core-2.5.2 lib + Sample cache配置

    **Ehcache Core 2.5.2:基础与Sample Cache配置详解** Ehcache是一款高效、开源的Java缓存库,广泛应用于提高应用程序性能,减少数据库负载。在这个压缩包中,我们关注的是Ehcache的核心组件ehcache-core-2.5.2,...

    Ehcache缓存

    默认情况下,Hibernate提供了第一级缓存——Session缓存,但对于更复杂的缓存需求,如跨Session的共享缓存,Ehcache则作为第二级缓存被引入。第二级缓存可以存储更多数据,减少对数据库的依赖,提高并发性能。 **...

    Ehcache经典中文教程

    Ehcache 还提供了多种操作,如更新元素 (`cache.put(new Element("key7", "NewValue7"))`)、检查缓存大小 (`cache.getSize()`)、磁盘持久化 (`cache.flush()`) 等。 Ehcache 支持多种缓存策略,如 LRU(Least ...

    Ehcache使用

    `CacheManager` 是 Ehcache 中的核心管理类,负责管理所有的缓存实例(`Cache`)。通常情况下,一个应用程序会有一个 `CacheManager` 实例,用于创建和管理缓存。 - **初始化方式**: - 直接调用 `CacheManager....

    spring+ehcache demo

    总结,这个"spring+ehcache demo"是一个全面的示例,涵盖了从引入Ehcache依赖、配置Spring Cache到编写缓存注解的方法。通过学习和实践这个示例,开发者可以深入了解Spring与Ehcache的集成,以及如何利用缓存提升...

    mybatis ehcache 1.0 ehcache.xsd 提示文件

    &lt;cache type="org.mybatis.cache.ehcache.EhcacheCache"&gt; &lt;property name="configLocation" value="classpath:ehcache.xml"/&gt; &lt;/cache&gt; ... ``` 在MyBatis的Mapper接口或XML映射文件中,我们还可以针对特定的...

    Ehcache_Hello

    Ehcache的核心概念包括缓存、缓存区(Cache)、缓存项(Cache Entry)和缓存策略。缓存区是Ehcache中存储数据的地方,每个缓存区都有自己的配置,如大小、过期时间等。缓存项则是缓存区中的具体数据单元,包含键值对...

    Ehcache整合Spring使用页面、对象缓存

    &lt;bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&gt; &lt;property name="configLocation" value="classpath:ehcache.xml"/&gt; &lt;cache:annotation-driven cache-manager=...

    ehcache.jar及源码

    6. **分布式缓存(Distributed Caching)**:Ehcache可以通过网络在多台机器之间共享缓存数据,实现分布式缓存,提高系统的可扩展性。 7. **事件监听(Event Listeners)**:Ehcache支持添加监听器来监控缓存操作,例如...

    ehcache

    - **Cache**: 在 Ehcache 中,每个缓存都是一个独立的命名空间,用于存储键值对。 - **Element**: Cache 中的每个条目被称为 Element,包含一个键(Key)和一个值(Value)以及相关的元数据(如有效期、大小等)。 ...

    EHcache相关jar下载及案例

    EHcache是一款广泛使用的开源Java分布式缓存系统,主要设计用于提高应用程序的性能和可伸缩性。在Java应用程序中,特别是那些基于Hibernate或MyBatis的持久层框架中,EHcache作为二级缓存工具,能够显著提升数据访问...

    Spring+Ehcache集成

    **Spring与Ehcache集成详解** ...&lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"&gt; &lt;property name="configLocation" value="classpath:ehcache.xml"/&gt; ...

Global site tag (gtag.js) - Google Analytics