??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲av永久综合在线观看尤物,亚洲av无码专区首页,国产亚洲女在线线精品http://m.tkk7.com/DLevin/category/54910.htmlIn general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatrazh-cnWed, 12 Aug 2015 23:15:00 GMTWed, 12 Aug 2015 23:15:00 GMT60深入Guava源码之Stripehttp://m.tkk7.com/DLevin/archive/2013/12/25/407990.htmlDLevinDLevinWed, 25 Dec 2013 02:03:00 GMThttp://m.tkk7.com/DLevin/archive/2013/12/25/407990.htmlhttp://m.tkk7.com/DLevin/comments/407990.htmlhttp://m.tkk7.com/DLevin/archive/2013/12/25/407990.html#Feedback3http://m.tkk7.com/DLevin/comments/commentRss/407990.htmlhttp://m.tkk7.com/DLevin/services/trackbacks/407990.html当前JDK对ƈ发编E的支持 Sun在Java5中引入了concurrent包,它对Java的ƈ发编E提供了强大的支持。首先,它提供了Lock接口Q可用了更细_度的控刉的区域,它的实现cLReentrantLockQReadLockQWriteLockQ其中ReadLock和WriteLock共同用于实现ReetrantReadWriteLockQ它l承自ReadWriteLockQ但是没有实现Lock接口QReadWriteLock接口也没有承Lock接口Q。而且Q它q提供了一些常用ƈ发场景下的类工具QSemaphore、CountDownLatch和CyclicBarrier。它们个字的应用场景Q?br />
  1. SemaphoreQ信号量Q?br /> 有n个非U程安全的资源(资源池)Q这些资源用一个SemaphoreQ计C号量Q保护,每个U程在用这些资源时需要首先获得一个信号量QacquireQ表C当前资源池q有可用资源Q然后线E从该资源池中获取ƈU除一个资源,在用完后,该资源交回l资源池Qƈ释放已经获得信号量(releaseQ(q里?#8220;U除”?#8220;交回”q不一定需要显C操作,只是一UŞ象的描述Q之所以这么描q是应ؓq里的各个资源是一LQ因而对一个线E它每次拿到的资源不一定是同一个资源,用于区分Stripe的用场景)Q其中Pool是一U典型的应用?/li>
  2. CountDownLatchQ闭锁)
    有n个TaskQ它们执行完成后需要执行另外一个收TaskQAggregated TaskQ,比如在做Report计算中,有n个Report要计,而在所有Report计算完成后需要生成一个基于所有Reportl果的一个ȝReportQ而这个ȝReport需要等到所有Report计算出结果后才能开始,此时可以定义一个CountDownLatchQ其初始值是nQ在ȝReport计算前调用CountDownLatch的awaitҎ{待其他Report执行完成Q而其他Report在完成后都会调用CountDownLatch中的countDownҎ?/li>
  3. CyclicBarrierQ关卡)
    每个U程执行完成后需要等待,直到n个线E都执行完成后,才能l箋执行Q在n个线E执行完成之后,而下一ơ执行开始之前可以添加自定义逻辑Q通过构徏CyclicBarrier实例时传入一个Runnable实例自定义逻辑Q,卛_每个U程执行完成后调用CyclicBarrier的awaitҎq等待(x谓的兛_Q,当n个线E都完成后,自定义的Runnable实例会自动被执行Q如果存在这LRunnable实例的话Q,然后所有线El下一ơ执行。这个现实中的例子没有想到比较合适的。。。?/li>
  4. ExchangerQ交换者)
    Exchanger是一U特D的CyclicBarrierQ它只有两个U程参与Q一个生产者,一个消费者,有两个队列共同参与,生者和消费者各自有一个队列,其中生者向它的队列d数据Q而消费者从它包含的队列中拿数据Q当生者中的队列满时调用exchangeҎQ传入自己原有的队列Q期待交换得到消费者中I的队列Q而当消费者中的队列满时同栯用exchangeҎQ传入自q原有队列Q期待获取到生者中已经填满的队列。这P生者和消费者可以和谐的生消费Qƈ且它们的步骤是一致的Q不哪一Ҏ另一方快都会{待另一方)?/li>
最后,Java5中还提供了一些atomiccM实现单场景下高效非lock方式的线E安全,以及BlockingQueue、Synchronizer、CompletionService、ConcurrentHashMap{工L?br />
在这里需要特别添加对ConcurrentHashMap的描qͼ因ؓGuava中的Stripe是对ConcurrentHashMap实现思想的抽象。在?a id="viewpost1_TitleUrl" href="http://m.tkk7.com/DLevin/archive/2013/10/18/405030.html" style="text-decoration: none; color: #4371a6; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 15px; font-weight: bold; line-height: 20px; background-color: #ffffff;">Java Corepd之ConcurrentHashMap实现(JDK 1.7)》一文中已经详细讲述了ConcurrentHashMap的实玎ͼ我们都知道ConcurrentHashMap的实现是ZSegment的,它内部包含了多个SegmentQ因而它内部的锁是基于Segment而不是整个MapQ从而减了锁的_度Q提升了性能。而这U分D锁不仅仅在HashMap用到?br />

Stripe的应用场?

虽然JDK中已lؓ我们提供了很多用于ƈ发编E的工具c,但是它ƈ没有提供对以下应用场景的支持Q有n个资源,我们希望Ҏ个资源的操作都是U程安全的,q里我们不能用SemaphoreQ因为Semaphore是一个池的概念,它所理的资源是同质的,比如从数据库的连接池中获取Connection操作的一U实现方式是内部保存一个Semaphore变量Q在每次获取ConnectionӞ先调用Semaphore的acquireҎ以保证连接池中还有空闲的ConnectionQ如果有Q则可以随机的选择一个Connection实例Q当Connection实例q回Ӟ该Connection实例必须从空闲列表中U除Q从而保证只有一个线E获取到ConnectionQ以保证一ơ只有一个线E用一个ConnectionQ在Java中数据库的Connection是线E安全,但是我们在用时依然会用q接池的方式创徏多个Connection而不是在一个应用程序中只用一个Connection是因为有些数据库厂商在实现ConnectionӞ一个Connection内的所有操作都时串行的Q而不是ƈ行的Q比如MySQL的Connection实现Q因而ؓ了提升ƈ行性,采用多个Connection方式Q。而这里的需求是Ҏ个资源的操作都是U程安全的,比如对JDK中HashMap的实现采用一个数l链表的l构Q参考?a id="viewpost1_TitleUrl" href="http://m.tkk7.com/DLevin/archive/2013/10/15/404984.html" style="text-decoration: none; color: #4371a6; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 15px; font-weight: bold; line-height: 20px; background-color: #ffffff;">Java Corepd之HashMap实现》)Q如果我们将链表作ؓ一个资源单位(q里的链表资源和上述的数据库q接资源是不一LQ对数据库连接每个线E只需要拿CQ意一个Connection实例卛_Q而这里的链表资源则是不同链表是不一LQ因而对每个操作Q我们需要获取特定的链表Q然后对链表以线E安全的方式操作Q因里多个线E会对同一个链表同时操作)Q那么ؓ了保证对各个单独链表操作的线E安全(如HashMap的put操作Q不考虑rehash的情况,有些其他操作需要更大粒度的U程安全Q比如contains{)Q其中一U简单的实现方式是ؓ每条链表兌一个锁Q对每条链表的读写操作用其兌锁即可。然而如果链表很多,需要用很多锁Q会消耗很多资源,虽然它的锁粒度最,q发性很高。然而如果各个链表之间没有很高的q发性,我们可以让多个链表׃n一个锁以减锁的用量Q虽然增大了锁的_度Q但是如果这些链表的q发E度q不是很高,那增大的锁的_度对ƈ发性ƈ没有很大的媄响?br />
在实际应用中Q我们有一个CachepȝQ它包含key和payload的键值对QMapQ,在Cache中Map的实现已l是U程安全了,然而我们不仅仅是向Cache中写数据要保证线E安全,在操作payloadӞ也需要保证线E安全。因为我们在Cache中的数据量很大,为每个payload配置一个单独的锁显然不现实Q也不需要因为它们没有那么高的ƈ发行Q因而我们需要一U机制将key分成不同的groupQ而每个group׃n一个锁Q这是ConcurrentHashMap的实现思\Q。通过key卛_获得一个锁Qƈ且每个相同的key获得的锁实例是相同的Q获得相同锁实例的key它们不一定相{,因ؓq是一对多的关p)?br />

Stripe的简单实?/strong>

Ҏ以上应用场景QStripe的实现很单,只需要内部保存一个Lock数组Q对每个l定的keyQ计其hash|ҎhashD其锁对应的数组下标Q而该下标下的Lock实例既是和该key兌的Lock实例。这里通过hash值把key和Lock实例兌hQؓ了扩展性,在实现时q可以把计算数组下标的逻辑抽象成一个接口,用户可以通过传入自定义该接口的实现类实例加入用户自定义的兌逻辑Q默认采用hash值关联方式?br />

Stripe在Guava中的实现

在Guava中,Stripe以抽象类的Ş式存在,它定义了通过l定key或index获得相应Lock/Semaphore/ReadWriteLock实例Q?br />
public abstract class Striped<L> {
  /**
   * Returns the stripe that corresponds to the passed key. It is always guaranteed that if
   * {
@code key1.equals(key2)}, then {@code get(key1) == get(key2)}.
   *
   * 
@param key an arbitrary, non-null key
   * 
@return the stripe that the passed key corresponds to
   
*/
  public abstract L get(Object key);

  /**
   * Returns the stripe at the specified index. Valid indexes are 0, inclusively, to
   * {
@code size()}, exclusively.
   *
   * 
@param index the index of the stripe to return; must be in {@code [0size())}
   * 
@return the stripe at the specified index
   
*/
  public abstract L getAt(int index);

  /**
   * Returns the index to which the given key is mapped, so that getAt(indexFor(key)) == get(key).
   
*/
  abstract int indexFor(Object key);

  /**
   * Returns the total number of stripes in this instance.
   
*/
  public abstract int size();

  /**
   * Returns the stripes that correspond to the passed objects, in ascending (as per
   * {
@link #getAt(int)}) order. Thus, threads that use the stripes in the order returned
   * by this method are guaranteed to not deadlock each other.
   *
   * <p>It should be noted that using a {
@code Striped<L>} with relatively few stripes, and
   * {
@code bulkGet(keys)} with a relative large number of keys can cause an excessive number
   * of shared stripes (much like the birthday paradox, where much fewer than anticipated birthdays
   * are needed for a pair of them to match). Please consider carefully the implications of the
   * number of stripes, the intended concurrency level, and the typical number of keys used in a
   * {
@code bulkGet(keys)} operation. See <a href="http://www.mathpages.com/home/kmath199.htm">Balls
   * in Bins model</a> for mathematical formulas that can be used to estimate the probability of
   * collisions.
   *
   * 
@param keys arbitrary non-null keys
   * 
@return the stripes corresponding to the objects (one per each object, derived by delegating
   *         to {
@link #get(Object)}; may contain duplicates), in an increasing index order.
   
*/
  public Iterable<L> bulkGet(Iterable<?> keys);
}
可以使用一下几个静态工厂方法创建相应的Striped实例Q其中lazyWeakXXX创徏的Striped实例中锁以弱引用的方式存在(在什么样的场景中使用呢?Q:
/**
 * Creates a {
@code Striped<Lock>} with eagerly initialized, strongly referenced locks.
 * Every lock is reentrant.
 *
 * 
@param stripes the minimum number of stripes (locks) required
 * 
@return a new {@code Striped<Lock>}
 
*/
public static Striped<Lock> lock(int stripes);
/**
 * Creates a {
@code Striped<Lock>} with lazily initialized, weakly referenced locks.
 * Every lock is reentrant.
 *
 * 
@param stripes the minimum number of stripes (locks) required
 * 
@return a new {@code Striped<Lock>}
 
*/
public static Striped<Lock> lazyWeakLock(int stripes);
/**
 * Creates a {
@code Striped<Semaphore>} with eagerly initialized, strongly referenced semaphores,
 * with the specified number of permits.
 *
 * 
@param stripes the minimum number of stripes (semaphores) required
 * 
@param permits the number of permits in each semaphore
 * 
@return a new {@code Striped<Semaphore>}
 
*/
public static Striped<Semaphore> semaphore(int stripes, final int permits);
/**
 * Creates a {
@code Striped<Semaphore>} with lazily initialized, weakly referenced semaphores,
 * with the specified number of permits.
 *
 * 
@param stripes the minimum number of stripes (semaphores) required
 * 
@param permits the number of permits in each semaphore
 * 
@return a new {@code Striped<Semaphore>}
   
*/
public static Striped<Semaphore> lazyWeakSemaphore(int stripes, final int permits);
/**
 * Creates a {
@code Striped<ReadWriteLock>} with eagerly initialized, strongly referenced
 * read-write locks. Every lock is reentrant.
 *
 * 
@param stripes the minimum number of stripes (locks) required
 * 
@return a new {@code Striped<ReadWriteLock>}
 
*/
public static Striped<ReadWriteLock> readWriteLock(int stripes);
/**
 * Creates a {
@code Striped<ReadWriteLock>} with lazily initialized, weakly referenced
 * read-write locks. Every lock is reentrant.
 *
 * 
@param stripes the minimum number of stripes (locks) required
 * 
@return a new {@code Striped<ReadWriteLock>}
 
*/
public static Striped<ReadWriteLock> lazyWeakReadWriteLock(int stripes);

Striped有两个具体实现类QCompactStriped和LazyStripedQ他们都l承自PowerOfTwoStripedQ用于表辑ֆ部保存的stripes值是2的指数|。PowerOfTwoStriped实现了indexFor()ҎQ它使用hash值做映射函数Q?br />
  private abstract static class PowerOfTwoStriped<L> extends Striped<L> {
    /** Capacity (power of two) minus one, for fast mod evaluation */
    final int mask;

    @Override final int indexFor(Object key) {
      int hash = smear(key.hashCode());
      return hash & mask;
    }
  }
  private static int smear(int hashCode) {
    hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
    return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4);
  }
CompactStripedcM用一个数l保存所有的Lock/Semaphore/ReadWriteLock实例Q在初始化时徏立所有的锁实例;而LazyStripedcM用一个gؓWeakReference的ConcurrentMap做ؓ数据l构QindexgؓkeyQLock/Semaphore/ReadWriteLock的WeakReference为|所有锁实例在用到时动态创建。在CompactStriped中创建锁实例时对ReentrantLock/Semaphore创徏采用PaddedXXX版本Q不知道Z要做Pad?br />
Stripedcd现的cd如下Q?br />

DLevin 2013-12-25 10:03 发表评论
]]>
Java Cachepd之Guava Cache实现详解http://m.tkk7.com/DLevin/archive/2013/10/20/404847.htmlDLevinDLevinSat, 19 Oct 2013 16:17:00 GMThttp://m.tkk7.com/DLevin/archive/2013/10/20/404847.htmlhttp://m.tkk7.com/DLevin/comments/404847.htmlhttp://m.tkk7.com/DLevin/archive/2013/10/20/404847.html#Feedback3http://m.tkk7.com/DLevin/comments/commentRss/404847.htmlhttp://m.tkk7.com/DLevin/services/trackbacks/404847.html
在Guava CacheBuilder的注释中l定Guava Cache以下的需求:
  1. automatic loading of entries into the cache
  2. least-recently-used eviction when a maximum size is exceeded
  3. time-based expiration of entries, measured since last access or last write
  4. keys automatically wrapped in WeakReference
  5. values automatically wrapped in WeakReference or SoftReference soft
  6. notification of evicted (or otherwise removed) entries
  7. accumulation of cache access statistics
对于q样的需求,如果要我们自己来实现Q我们应该怎么设计Q对于我来说Q对于其核心实现我会做如下的设计Q?
  1. 定义一个CacheConfigcȝ于纪录所有的配置Q如CacheLoaderQmaximum size、expire time、key reference level、value reference level、eviction listener{?/li>
  2. 定义一个Cache接口Q该接口cMMapQ或ConcurrentMapQ,但是Z和Map区别开来,因而重新定义一个Cache接口?/li>
  3. 定义一个实现Cache接口的类CacheImplQ它接收CacheConfig作ؓ参数的构造函敎ͼq将CacheConfig实例保存在字D中?/li>
  4. 在实C模仿ConcurrentHashMap的实现方式,有一个Segment数组Q其长度由配|的concurrencyLevel值决定。ؓ了实现最q最用算法(LRUQ,dAccessQueue和WriteQueue字段Q这两个Queue内部采用双链表,每次新创Z个EntryQ就这个Entry加入到这两个Queue的末,而每d一个Entry将其添加到AccessQueue的末,没更C个Entry该Entryd到WriteQueue末尾。ؓ了实现key和value上的WeakReference、SoftReferenceQ添加ReferenceQueue<K>cd的keyReferenceQueue和valueReferenceQueue字段?/li>
  5. 在每ơ调用方法之前都遍历AccessQueue和WriteQueueQ如果发现有Entry已经expireQ就该Entry从这两个Queue上和Cache中移除。然后遍历keyReferenceQueue和valueReferenceQ如果发现有存在,同样它们移除。在U除时如果有EvictionListener注册着Q则调用该listener?/li>
  6. 对Segment实现Q它时一个CacheEntry数组QCacheEntry是一个链节点Q它包含hash、key、vlaue、next。CacheEntryҎ是否需要包装在WeakReference中创建WeakEntry或StrongEntryQ而对valueҎ是否需要包装在WeakReference、SoftReference中创建WeakValueReference、SoftValueReference、StrongValueReference。在get操作中对于需要用CacheLoader加蝲的值先d一个具有LoadingValueReference值的EntryQ这样可以保证同一个Key只加载依ơ。在加蝲成功后将LoadingValueReferenceҎ配置替换成其他Weak、Soft、Strong ValueReference?br />
  7. 对于cache access statisticsQ只需要有一个类在需要的地方做一些统计计数即可?/li>
  8. 最后我必须得承认以上的设计有很多是对Guava Cache的参考,我有点后悔没有在看源码之前考虑q个问题Q等看过以后思\p它的实现l羁l了。。。?/li>
Guava Cache的数据结?/strong>
因ؓ新进一家公司,要熟悉新公司目以及目用到的第三方库的代码Q因而几个月来看了许多代码。然后越来越发现要理解一个项目的最快方法是先搞清楚该项目的底层数据l构Q然后再ȝ构徏于这些数据结构以上的逻辑׃Ҏ许多。记得在q是学生的时候,有在一本书上看到过一个大牛说的一句话Q程序=数据l构Q算法;当时对这句话q不是和理解Q现在是很赞同这句话Q我对算法接触的不多Q因而我更們֐于将q里的算法理解长控制数据动的逻辑。因而我们先来熟悉一下Guava Cache的数据结构?br />
CachecM于MapQ它是存储键值对的集合,然而它和Map不同的是它还需要处理evict、expire、dynamic load{逻辑Q需要一些额外信息来实现q些操作。在面向对象思想中,l常使用cd一些关联性比较强的数据做装Q同时把操作q些数据相关的操作放到该cM。因而Guava Cache使用ReferenceEntry接口来封装一个键值对Q而用ValueReference来封装Value倹{这里之所以用Reference命oQ是因ؓGuava Cache要支持WeakReference Key和SoftReference、WeakReference value?br />
ValueReference
对于ValueReferenceQ因为Guava Cache支持强引用的Value、SoftReference Value以及WeakReference ValueQ因而它对应三个实现c:StrongValueReference、SoftValueReference、WeakValueReference。ؓ了支持动态加载机Ӟ它还有一个LoadingValueReferenceQ在需要动态加载一个key的值时Q先把该值封装在LoadingValueReference中,以表达该key对应的值已l在加蝲了,如果其他U程也要查询该key对应的|p得到该引用,q且{待改值加载完成,从而保证该值只被加载一ơ(可以在evict以后重新加蝲Q。在该只加蝲完成后,LoadingValueReference替换成其他ValueReferencecd。对新创建的LoadingValueReferenceQ由于其内部oldValue的初始值是UNSETQ它isActive为falseQisLoading为falseQ因而此时的LoadingValueReference的isActive为falseQ但是isLoading为true。每个ValueReference都纪录了weight|所谓weight从字面上理解?#8220;该值的重量”Q它由Weighter接口计算而得。weight在Guava Cache中由两个用途:1. 对weightgؓ0Ӟ在计因为size limit而evict是忽略该EntryQ它可以通过其他机制evictQ;2. 如果讄了maximumWeight|则当Cache中weight和超q了该值时Q就会引起evict操作。但是目前还不知道这个设计的用途。最后,Guava Cacheq定义了Stength枚Dcd作ؓValueReference的factoryc,它有三个枚D|Strong、Soft、WeakQ这三个枚D值分别创建各自的ValueReferenceQƈ且根据传入的weight值是否ؓ1而决定是否要创徏Weight版本的ValueReference。以下是ValueReference的类图:

q里ValueReference之所以要有对ReferenceEntry的引用是因ؓ在Value因ؓWeakReference、SoftReference被回收时Q需要用其key对应的从Segment的table中移除;copyFor()函数的存在是因ؓ在expand(rehash)重新创徏节点Ӟ对WeakReference、SoftReference需要重新创建实例(个h感觉是ؓ了保持对象状态不会相互媄响,但是不确定是否还有其他原因)Q而对强引用来_直接使用原来的值即可,q里很好的展CZ对彼变化的封装思想QnotifiyNewValue只用于LoadingValueReferenceQ它的存在是Z对LoadingValueReference来说能更加及时的得到CacheLoader加蝲的倹{?br />
ReferenceEntry
ReferenceEntry是Guava Cache中对一个键值对节点的抽象。和ConcurrentHashMap一PGuava Cache由多个Segmentl成Q而每个Segment包含一个ReferenceEntry数组Q每个ReferenceEntry数组w是一条ReferenceEntry链。ƈ且一个ReferenceEntry包含key、hash、valueReference、next字段。除了在ReferenceEntry数组中l成的链Q在一个Segment中,所有ReferenceEntryq组成access链(accessQueueQ和write链(writeQueueQ,q两条都是双向链表,分别通过previousAccess、nextAccess和previousWrite、nextWrite字段链接而成。在Ҏ个节点的更新操作都会该节点重新铑ֈwrite铑֒access链末,q且更新其writeTime和accessTime字段Q而没扑ֈ一个节点,都会该节点重新铑ֈaccess链末,q更新其accessTime字段。这两个双向链表的存在都是ؓ了实现采用最q最用算法(LRUQ的evict操作Qexpire、size limit引v的evictQ?br />
Guava Cache中的ReferenceEntry可以是强引用cd的keyQ也可以WeakReferencecd的keyQؓ了减内存用量Q还可以Ҏ是否配置了expireAfterWrite、expireAfterAccess、maximumSize来决定是否需要write铑֒access铄定要创徏的具体ReferenceQStrongEntry、StrongWriteEntry、StrongAccessEntry、StrongWriteAccessEntry{。创Z同类型的ReferenceEntry由其枚D工厂cEntryFactory来实玎ͼ它根据key的Strongthcd、是否用accessQueue、是否用writeQueue来决定不同的EntryFactry实例Qƈ通过它创建相应的ReferenceEntry实例。ReferenceEntrycd如下Q?

WriteQueue和AccessQueue
Z实现最q最用算法,Guava Cache在Segment中添加了两条链:write链(writeQueueQ和access链(accessQueueQ,q两条链都是一个双向链表,通过ReferenceEntry中的previousInWriteQueue、nextInWriteQueue和previousInAccessQueue、nextInAccessQueue链接而成Q但是以Queue的Ş式表达。WriteQueue和AccessQueue都是自定义了offer、addQ直接调用offerQ、remove、poll{操作的逻辑Q对于offerQaddQ操作,如果是新加的节点Q则直接加入到该铄l尾Q如果是已存在的节点Q则该节点链接的链;对remove操作Q直接从该链中移除该节点Q对poll操作Q将头节点的下一个节点移除,q返回?br />
  static final class WriteQueue<K, V> extends AbstractQueue<ReferenceEntry<K, V>> {
    final ReferenceEntry<K, V> head = new AbstractReferenceEntry<K, V>() ....
    @Override
    public boolean offer(ReferenceEntry<K, V> entry) {
      // unlink
      connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue());
      // add to tail
      connectWriteOrder(head.getPreviousInWriteQueue(), entry);
      connectWriteOrder(entry, head);
      return true;
    }
    @Override
    public ReferenceEntry<K, V> peek() {
      ReferenceEntry<K, V> next = head.getNextInWriteQueue();
      return (next == head) ? null : next;
    }
    @Override
    public ReferenceEntry<K, V> poll() {
      ReferenceEntry<K, V> next = head.getNextInWriteQueue();
      if (next == head) {
        return null;
      }
      remove(next);
      return next;
    }
    @Override
    public boolean remove(Object o) {
      ReferenceEntry<K, V> e = (ReferenceEntry) o;
      ReferenceEntry<K, V> previous = e.getPreviousInWriteQueue();
      ReferenceEntry<K, V> next = e.getNextInWriteQueue();
      connectWriteOrder(previous, next);
      nullifyWriteOrder(e);
      return next != NullEntry.INSTANCE;
    }
    @Override
    public boolean contains(Object o) {
      ReferenceEntry<K, V> e = (ReferenceEntry) o;
      return e.getNextInWriteQueue() != NullEntry.INSTANCE;
    }
....
  }

对于不需要维护WriteQueue和AccessQueue的配|(x有expire time或size limit的evict{略Q来_我们可以使用DISCARDING_QUEUE以节省内存:
  static final Queue<? extends Object> DISCARDING_QUEUE = new AbstractQueue<Object>() {
    @Override
    public boolean offer(Object o) {
      return true;
    }
    @Override
    public Object peek() {
      return null;
    }
    @Override
    public Object poll() {
      return null;
    }
....
  };


Segment中的evict
在解决了所有数据结构的问题以后Q让我们来看看LocalCache中的核心cSegment的实玎ͼ首先从evict开始。在Guava Cache的evict时机上,它没有用另一个后台线E每隔一D|间扫瞄一ơtable以evict那些已经expire的entry。而是它在每次操作开始和l束时才做一遍清理工作,q样可以减少开销Q但是如果长旉不调用方法的话,会引h些entry不能及时被evict出去。evict主要处理四个QueueQ?. keyReferenceQueueQ?. valueReferenceQueueQ?. writeQueueQ?. accessQueue。前两个queue是因为WeakReference、SoftReference被垃圑֛收时加入的,清理时只需要遍历整个queueQ将对应的项从LocalCache中移除即可,q里keyReferenceQueue存放ReferenceEntryQ而valueReferenceQueue存放的是ValueReferenceQ要从LocalCache中移除需要有keyQ因而ValueReference需要有对ReferenceEntry的引用。这里的U除通过LocalCache而不是Segment是因为在U除时因为expandQrehashQ可能导致原来在某个Segment中的ReferenceEntry后来被移动到另一个Segment中了。而对后两个QueueQ只需要检查是否配|了相应的expire旉Q然后从头开始查扑ַlexpire的EntryQ将它们U除卛_。有不同的是在移除时Q还会注册移除的事gQ这些事件将会在接下来的操作调用注册的RemovalListener触发Q这些代码比较简单,不详q?br />在put的时候,q会清理recencyQueueQ即recencyQueue中的Entryd到accessEntry中,此时可能会发生某个Entry实际上已l被U除了,但是又被d回accessQueue中了Q这U情况下Q如果没有用WeakReference、SoftReferenceQ也没有配置expire旉Q则会引起一些内存泄漏问题。recencyQueue在get操作时被dQ但是ؓ什么会有这个Queue的存在一直没有想明白?br />
Segment中的put操作
put操作相对比较单,首先它需要获得锁Q然后尝试做一些清理工作,接下来的逻辑cMConcurrentHashMap中的rehashQ不详述。需要说明的是当扑ֈ一个已存在的EntryӞ需要先判断当前的ValueRefernece中的g实上已经被回收了Q因为它们可以时WeakReference、SoftReferencecdQ如果已l被回收了,则将新值写入。ƈ且在每次更新时注册当前操作引LU除事gQ指定相应的原因QCOLLECTED、REPLACED{,q些注册的事件在退出的时候统一调用LocalCache注册的RemovalListenerQ由于事件处理可能会有很长时_因而这里将事g处理的逻辑在退出锁以后才做。最后,在更新已存在的Entryl束后都试着那些已lexpire的EntryU除。另外put操作中还需要更新writeQueue和accessQueue的语义正性?br />
    V put(K key, int hash, V value, boolean onlyIfAbsent) {
      ....
        for (ReferenceEntry<K, V> e = first; e != null; e = e.getNext()) {
          K entryKey = e.getKey();
          if (e.getHash() == hash && entryKey != null && map.keyEquivalence.equivalent(key, entryKey)) {
            ValueReference<K, V> valueReference = e.getValueReference();
            V entryValue = valueReference.get();
            if (entryValue == null) {
              ++modCount;
              if (valueReference.isActive()) {
                enqueueNotification(key, hash, valueReference, RemovalCause.COLLECTED);
                setValue(e, key, value, now);
                newCount = this.count; // count remains unchanged
              } else {
                setValue(e, key, value, now);
                newCount = this.count + 1;
              }
              this.count = newCount; // write-volatile
              evictEntries();
              return null;
            } else if (onlyIfAbsent) {
              recordLockedRead(e, now);
              return entryValue;
            } else {
              ++modCount;
              enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED);
              setValue(e, key, value, now);
              evictEntries();
              return entryValue;
            }
          }
        }
...
      } finally {
        ...
        postWriteCleanup();
      }
    }

Segment带CacheLoader的get操作
q部分的代码有点不知道怎么说了Q大概上的步骤是Q?. 先查找table中是否已存在没有被回收、也没有expire的entryQ如果找刎ͼq在CacheBuilder中配|了refreshAfterWriteQƈ且当前时间间隔已l操作这个事Ӟ则重新加载|否则Q直接返回原有的|2. 如果查找到的ValueReference是LoadingValueReferenceQ则{待该LoadingValueReference加蝲l束Qƈq回加蝲的|3. 如果没有扑ֈentryQ或者找到的entry的gؓnullQ则加锁后,l箋table中已存在key对应的entryQ如果找到ƈ且对应的entry.isLoading()为trueQ则表示有另一个线E正在加载,因而等待那个线E加载完成,如果扑ֈ一个非null|q回该|否则创徏一个LoadingValueReferenceQƈ调用loadSync加蝲相应的|在加载完成后Q将新加载的值更新到table中,卛_部分情况下替换原来的LoadingValueReference?br />
Segment中的其他操作
其他操作包括不含CacheLoader的get、containsKey、containsValue、replace{操作逻辑重复性很大,而且和ConcurrentHashMap的实现方式也cMQ不在详q?br />
Cache StatsCounter和CacheStats
ZU录Cache的用情况,如果命中ơ数、没有命中次数、evictơ数{,Guava Cache中定义了StatsCounter做这些统计信息,它有一个简单的SimpleStatsCounter实现Q我们也可以通过CacheBuilder配置自己的StatsCounter?br />
  public interface StatsCounter {
    public void recordHits(int count);
    public void recordMisses(int count);
    public void recordLoadSuccess(long loadTime);
    public void recordLoadException(long loadTime);
    public void recordEviction();

    public CacheStats snapshot();
  }
在得到StatsCounter实例后,可以使用CacheStats获取具体的统计信息:
public final class CacheStats {
  private final long hitCount;
  private final long missCount;
  private final long loadSuccessCount;
  private final long loadExceptionCount;
  private final long totalLoadTime;
  private final long evictionCount;

}

同ConcurrentHashMapQ在知道Segment实现以后Q其他的Ҏ基本上都是代理给Segment内部ҎQ因而在LocalCachecM的其他方法看h比较容易理解,不在详述。然而Guava Cacheq没有将ConcurrentMap直接提供l用户用,而是Z区分Cache和MapQ它自定义了一个自qCache接口和LoadingCache接口Q我们可以通过CacheBuilder配置不同的参敎ͼ然后使用build()Ҏq回一个Cache或LoadingCache实例Q?br />
public interface Cache<K, V> {
  V getIfPresent(Object key);
  V get(K key, Callable<? extends V> valueLoader) throws ExecutionException;
  ImmutableMap<K, V> getAllPresent(Iterable<?> keys);
  void put(K key, V value);
  void putAll(Map<? extends K,? extends V> m);
  void invalidate(Object key);
  void invalidateAll(Iterable<?> keys);
  void invalidateAll();
  long size();
  CacheStats stats();
  ConcurrentMap<K, V> asMap();
  void cleanUp();
}

public interface LoadingCache<K, V> extends Cache<K, V>, Function<K, V> {
  V get(K key) throws ExecutionException;
  V getUnchecked(K key);
  ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException;
  V apply(K key);
  void refresh(K key);
  ConcurrentMap<K, V> asMap();
}


DLevin 2013-10-20 00:17 发表评论
]]>
վ֩ģ壺 Ѹ߹ۿ| Ʒһ߹ۿ| vaĻþò| ˾ѹۿ| Ƶ97 | &#228;v뾫Ʒþþ| þþþseɫ͵͵޾Ʒav | ޾Ʒav| **ëƬѹۿþþƷ| Ļav| һ| ݺɫۺվɫ| jizzjizzƵȫ| һƷƵ߲| պ޵һҳ| 97ѹۿƵ| ޵av| ȫɫƴɫƬѾþ| 鶹һ| ޾Ʒ޿| һƵ| պŷ| ͵͵߶̬ͼ| 95˾Ѹ| ޾ƷƬ| ޳aƬ߹ۿ| 91ڹӰ| һд| ٸ17p| ĻƵww | һƷ| ҪWWWѿƵ| ĻƷ| þúݺݸ߳޾Ʒ| һƷһaһ| պҹTVӰԺ| ҹWWWʪˬ| պAvҹҹˬ | Ʒѿþþþ| ƷƵ| 鵺̳߹ۿ|