标签:style blog http io ar color 使用 sp java
原文地址:http://www.blogjava.net/DLevin/archive/2013/10/15/404770.html
前记:最近公司在做的项目完全基于Cache(Gemfire)构建了一个类数据库的系统,自己做的一个小项目里用过Guava的Cache,以前做过的项目中使用过EHCache,既然和Cache那么有缘,那就趁这个机会好好研究一下Java中的Cache库。在Java社区中已经提供了很多Cache库实现,具体可以参考http://www.open-open.com/13.htm,这里只关注自己用到的几个Cache库而且这几个库都比较具有代表性:Guava中提供的Cache是基于单JVM的简单实现;EHCache出自Hibernate,也是基于单JVM的实现,是对单JVM Cache比较完善的实现;而Gemfire则提供了对分布式Cache的完善实现。这一系列的文章主要关注在这几个Cache系统的实现上,因而步探讨关于Cache的好处、何时用Cache等问题,由于他们都是基于内存的Cache,因而也仅局限于这种类型的Cache(说实话,我不知道有没有其他的Cache系统,比如基于文件?囧)。
记得我最早接触Cache是在大学学计算机组成原理的时候,由于CPU的速度要远大于内存的读取速度,为了提高CPU的效率,CPU会在内部提供缓存区,该缓存区的读取速度和CPU的处理速度类似,CPU可以直接从缓存区中读取数据,从而解决CPU的处理速度和内存读取速度不匹配的问题。缓存之所以能解决这个问题是基于程序的局部性原理,即”程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。局部性原理又表现为:时间局部性和空间局部性。时间局部性是指如果程序中的某条指令一旦执行,则不久之后该指令可能再次被执行;如果某数据被访问,则不久之后该数据可能再次被访问。空间局部性是指一旦程序访问了某个存储单元,则不久之后。其附近的存储单元也将被访问。”在实际工作中,CPU先向缓存区读取数据,如果缓存区已存在,则读取缓存中的数据(命中),否则(失效),将内存中相应数据块载入缓存中,以提高接下来的访问速度。由于成本和CPU大小的限制,CPU只能提供有限的缓存区,因而缓存区的大小是衡量CPU性能的重要指标之一。
使用缓存,在CPU向内存更新数据时需要处理一个问题(写回策略问题),即CPU在更新数据时只更新缓存的数据(write back,写回,当缓存需要被替换时才将缓存中更新的值写回内存),还是CPU在更新数据时同时更新缓存中和内存中的数据(write through,写通)。在写回策略中,为了减少内存写操作,缓存块通常还设有一个脏位(dirty bit),用以标识该块在被载入之后是否发生过更新。如果一个缓存块在被置换回内存之前从未被写入过,则可以免去回写操作;写回的优点是节省了大量的写操作。这主要是因为,对一个数据块内不同单元的更新仅需一次写操作即可完成。这种内存带宽上的节省进一步降低了能耗,因此颇适用于嵌入式系统。写通策略由于要经常和内存交互(有些CPU设计会在中间提供写缓冲器以缓解性能),因而性能较差,但是它实现简单,而且能简单的维持数据一致性。
在软件的缓存系统中,一般是为了解决内存的访问速率和磁盘、网络、数据库(属于磁盘或网络访问,单独列出来因为它的应用比较广泛)等访问速率不匹配的问题(对于内存缓存系统来说)。但是由于内存大小和成本的限制,我们又不能把所有的数据先加载进内存来。因而如CPU中的缓存,我们只能先将一部分数据保存在缓存中。此时,对于缓存,我们一般要解决如下需求:
在Java中,Map是最简单的Cache,为了高效的在多线程环境中使用,可以使用ConcurrentHashMap,这也正是我之前参与的一个项目中最开始的实现(后来引入EHCache)。为了语意更加清晰、保持接口的简单,下面我实现了一个基于Map的最简单的Cache系统,用以演示Cache的基本使用方式。用户可以向它提供数据、查询数据、判断给定Key的存在性、移除给定的Key(s)、清除整个Cache等操作。以下是Cache的接口定义。
1 public interface Cache<K, V> { 2 public String getName(); 3 public V get(K key); 4 public Map<? extends K, ? extends V> getAll(Iterator<? extends K> keys); 5 public boolean isPresent(K key); 6 public void put(K key, V value); 7 public void putAll(Map<? extends K, ? extends V> entries); 8 public void invalidate(K key); 9 public void invalidateAll(Iterator<? extends K> keys); 10 public void invalidateAll(); 11 public boolean isEmpty(); 12 public int size(); 13 public void clear(); 14 public Map<? extends K, ? extends V> asMap(); 15 }
这个简单的Cache实现只是对HashMap的封装,之所以选择HashMap而不是ConcurrentHashMap是因为在ConcurrentHashMap无法实现getAll()方法;并且这里所有的操作我都加锁了,因而也不需要ConcurrentHashMap来保证线程安全问题;为了提升性能,我使用了读写锁,以提升并发查询性能。因为代码比较简单,所以把所有代码都贴上了(懒得整理了。。。。)。
public class CacheImpl<K, V> implements Cache<K, V> { private final String name; private final HashMap<K, V> cache; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); public CacheImpl(String name) { this.name = name; cache = new HashMap<K, V>(); } public CacheImpl(String name, int initialCapacity) { this.name = name; cache = new HashMap<K, V>(initialCapacity); } public String getName() { return name; } public V get(K key) { readLock.lock(); try { return cache.get(key); } finally { readLock.unlock(); } } public Map<? extends K, ? extends V> getAll(Iterator<? extends K> keys) { readLock.lock(); try { Map<K, V> map = new HashMap<K, V>(); List<K> noEntryKeys = new ArrayList<K>(); while(keys.hasNext()) { K key = keys.next(); if(isPresent(key)) { map.put(key, cache.get(key)); } else { noEntryKeys.add(key); } } if(!noEntryKeys.isEmpty()) { throw new CacheEntriesNotExistException(this, noEntryKeys); } return map; } finally { readLock.unlock(); } } public boolean isPresent(K key) { readLock.lock(); try { return cache.containsKey(key); } finally { readLock.unlock(); } } public void put(K key, V value) { writeLock.lock(); try { cache.put(key, value); } finally { writeLock.unlock(); } } public void putAll(Map<? extends K, ? extends V> entries) { writeLock.lock(); try { cache.putAll(entries); } finally { writeLock.unlock(); } } public void invalidate(K key) { writeLock.lock(); try { if(!isPresent(key)) { throw new CacheEntryNotExistsException(this, key); } cache.remove(key); } finally { writeLock.unlock(); } } public void invalidateAll(Iterator<? extends K> keys) { writeLock.lock(); try { List<K> noEntryKeys = new ArrayList<K>(); while(keys.hasNext()) { K key = keys.next(); if(!isPresent(key)) { noEntryKeys.add(key); } } if(!noEntryKeys.isEmpty()) { throw new CacheEntriesNotExistException(this, noEntryKeys); } while(keys.hasNext()) { K key = keys.next(); invalidate(key); } } finally { writeLock.unlock(); } } public void invalidateAll() { writeLock.lock(); try { cache.clear(); } finally { writeLock.unlock(); } } public int size() { readLock.lock(); try { return cache.size(); } finally { readLock.unlock(); } } public void clear() { writeLock.lock(); try { cache.clear(); } finally { writeLock.unlock(); } } public Map<? extends K, ? extends V> asMap() { readLock.lock(); try { return new ConcurrentHashMap<K, V>(cache); } finally { readLock.unlock(); } } public boolean isEmpty() { readLock.lock(); try { return cache.isEmpty(); } finally { readLock.unlock(); } } }
其简单的使用用例如下:
1 @Test 2 public void testCacheSimpleUsage() { 3 Book uml = bookFactory.createUMLDistilled(); 4 Book derivatives = bookFactory.createDerivatives(); 5 6 String umlBookISBN = uml.getIsbn(); 7 String derivativesBookISBN = derivatives.getIsbn(); 8 9 Cache<String, Book> cache = cacheFactory.create("book-cache"); 10 cache.put(umlBookISBN, uml); 11 cache.put(derivativesBookISBN, derivatives); 12 13 Book fetchedBackUml = cache.get(umlBookISBN); 14 System.out.println(fetchedBackUml); 15 16 Book fetchedBackDerivatives = cache.get(derivativesBookISBN); 17 System.out.println(fetchedBackDerivatives); 18 }
【转载】Java Cache系列之Cache概述和Simple Cache
标签:style blog http io ar color 使用 sp java
原文地址:http://www.cnblogs.com/qiluoao13077220/p/4160416.html