﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-上善若水-随笔分类-Guava</title><link>http://www.blogjava.net/DLevin/category/54910.html</link><description>In 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 -Sinatra</description><language>zh-cn</language><lastBuildDate>Wed, 12 Aug 2015 23:15:00 GMT</lastBuildDate><pubDate>Wed, 12 Aug 2015 23:15:00 GMT</pubDate><ttl>60</ttl><item><title>深入Guava源码之Stripe</title><link>http://www.blogjava.net/DLevin/archive/2013/12/25/407990.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Wed, 25 Dec 2013 02:03:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/12/25/407990.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/407990.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/12/25/407990.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/407990.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/407990.html</trackback:ping><description><![CDATA[<h3><strong style="color: #ff9900;">当前JDK对并发编程的支持</strong></h3>
Sun在Java5中引入了concurrent包，它对Java的并发编程提供了强大的支持。首先，它提供了Lock接口，可用了更细粒度的控制锁的区域，它的实现类有ReentrantLock，ReadLock，WriteLock，其中ReadLock和WriteLock共同用于实现ReetrantReadWriteLock（它继承自ReadWriteLock，但是没有实现Lock接口，ReadWriteLock接口也没有继承Lock接口）。而且，它还提供了一些常用并发场景下的类工具：Semaphore、CountDownLatch和CyclicBarrier。它们个字的应用场景：<br />
<ol style="margin-top: 1px;">
     <li>Semaphore（信号量）<br />
     有n个非线程安全的资源（资源池），这些资源使用一个Semaphore（计数信号量）保护，每个线程在使用这些资源时需要首先获得一个信号量（acquire）表示当前资源池还有可用资源，然后线程从该资源池中获取并移除一个资源，在使用完后，将该资源交回给资源池，并释放已经获得信号量（release）（这里的&#8220;移除&#8221;、&#8220;交回&#8221;并不一定需要显示操作，只是一种形象的描述，之所以这么描述是应为这里的各个资源是一样的，因而对一个线程它每次拿到的资源不一定是同一个资源，用于区分Stripe的使用场景），其中Pool是一种典型的应用。</li>
     <li> CountDownLatch（闭锁）<br />
     有n个Task，它们执行完成后需要执行另外一个收尾的Task（Aggregated Task），比如在做Report计算中，有n个Report要计算，而在所有Report计算完成后需要生成一个基于所有Report结果的一个总的Report，而这个总的Report需要等到所有Report计算出结果后才能开始，此时就可以定义一个CountDownLatch，其初始值是n，在总的Report计算前调用CountDownLatch的await方法等待其他Report执行完成，而其他Report在完成后都会调用CountDownLatch中的countDown方法。</li>
     <li>CyclicBarrier（关卡）<br />
     每个线程执行完成后需要等待，直到n个线程都执行完成后，才能继续执行，在n个线程执行完成之后，而下一次执行开始之前可以添加自定义逻辑（通过构建CyclicBarrier实例时传入一个Runnable实例自定义逻辑），即在每个线程执行完成后调用CyclicBarrier的await方法并等待（即所谓的关卡），当n个线程都完成后，自定义的Runnable实例会自动被执行（如果存在这样的Runnable实例的话），然后所有线程继续下一次执行。这个现实中的例子没有想到比较合适的。。。。</li>
     <li>Exchanger（交换者）<br />
     Exchanger是一种特殊的CyclicBarrier，它只有两个线程参与，一个生产者，一个消费者，有两个队列共同参与，生产者和消费者各自有一个队列，其中生产者向它的队列添加数据，而消费者从它包含的队列中拿数据，当生产者中的队列满时调用exchange方法，传入自己原有的队列，期待交换得到消费者中空的队列；而当消费者中的队列满时同样调用exchange方法，传入自己的原有队列，期待获取到生产者中已经填满的队列。这样，生产者和消费者可以和谐的生产消费，并且它们的步骤是一致的（不管哪一方比另一方快都会等待另一方）。</li>
</ol>
最后，Java5中还提供了一些atomic类以实现简单场景下高效非lock方式的线程安全，以及BlockingQueue、Synchronizer、CompletionService、ConcurrentHashMap等工具类。<br />
<br />
在这里需要特别添加对ConcurrentHashMap的描述，因为Guava中的Stripe就是对ConcurrentHashMap实现思想的抽象。在《<a id="viewpost1_TitleUrl" href="http://www.blogjava.net/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 Core系列之ConcurrentHashMap实现(JDK 1.7)</a>》一文中已经详细讲述了ConcurrentHashMap的实现，我们都知道ConcurrentHashMap的实现是基于Segment的，它内部包含了多个Segment，因而它内部的锁是基于Segment而不是整个Map，从而减小了锁的粒度，提升了性能。而这种分段锁不仅仅在HashMap用到。<br />
<h3><strong style="color: #ff9900;">Stripe的应用场景
</strong></h3>虽然JDK中已经为我们提供了很多用于并发编程的工具类，但是它并没有提供对以下应用场景的支持：有n个资源，我们希望对每个资源的操作都是线程安全的，这里我们不能用Semaphore，因为Semaphore是一个池的概念，它所管理的资源是同质的，比如从数据库的连接池中获取Connection操作的一种实现方式是内部保存一个Semaphore变量，在每次获取Connection时，先调用Semaphore的acquire方法以保证连接池中还有空闲的Connection，如果有，则可以随机的选择一个Connection实例，当Connection实例返回时，该Connection实例必须从空闲列表中移除，从而保证只有一个线程获取到Connection，以保证一次只有一个线程使用一个Connection（在Java中数据库的Connection是线程安全，但是我们在使用时依然会用连接池的方式创建多个Connection而不是在一个应用程序中只用一个Connection是因为有些数据库厂商在实现Connection时，一个Connection内的所有操作都时串行的，而不是并行的，比如MySQL的Connection实现，因而为了提升并行性，采用多个Connection方式）。而这里的需求是对每个资源的操作都是线程安全的，比如对JDK中HashMap的实现采用一个数组链表的结构（参考《<a id="viewpost1_TitleUrl" href="http://www.blogjava.net/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 Core系列之HashMap实现</a>》），如果我们将链表作为一个资源单位（这里的链表资源和上述的数据库连接资源是不一样的，对数据库连接每个线程只需要拿到任意一个Connection实例即可，而这里的链表资源则是不同链表是不一样的，因而对每个操作，我们需要获取特定的链表，然后对链表以线程安全的方式操作，因为这里多个线程会对同一个链表同时操作），那么为了保证对各个单独链表操作的线程安全（如HashMap的put操作，不考虑rehash的情况，有些其他操作需要更大粒度的线程安全，比如contains等），其中一种简单的实现方式是为每条链表关联一个锁，对每条链表的读写操作使用其关联锁即可。然而如果链表很多，就需要使用很多锁，会消耗很多资源，虽然它的锁粒度最小，并发性很高。然而如果各个链表之间没有很高的并发性，我们就可以让多个链表共享一个锁以减少锁的使用量，虽然增大了锁的粒度，但是如果这些链表的并发程度并不是很高，那增大的锁的粒度对并发性并没有很大的影响。<br /><br />在实际应用中，我们有一个Cache系统，它包含key和payload的键值对（Map），在Cache中Map的实现已经是线程安全了，然而我们不仅仅是向Cache中写数据要保证线程安全，在操作payload时，也需要保证线程安全。因为我们在Cache中的数据量很大，为每个payload配置一个单独的锁显然不现实，也不需要因为它们没有那么高的并发行，因而我们需要一种机制将key分成不同的group，而每个group共享一个锁（这就是ConcurrentHashMap的实现思路）。通过key即可获得一个锁，并且每个相同的key获得的锁实例是相同的（获得相同锁实例的key它们不一定相等，因为这是一对多的关系）。<br /><h3><strong style="color: #ff9900;">Stripe的简单实现</strong></h3>根据以上应用场景，Stripe的实现很简单，只需要内部保存一个Lock数组，对每个给定的key，计算其hash值，根据hash值计算其锁对应的数组下标，而该下标下的Lock实例既是和该key关联的Lock实例。这里通过hash值把key和Lock实例关联起来，为了扩展性，在实现时还可以把计算数组下标的逻辑抽象成一个接口，用户可以通过传入自定义该接口的实现类实例加入用户自定义的关联逻辑，默认采用hash值关联方式。<br /><h3><strong style="color: #ff9900;">Stripe在Guava中的实现</strong></h3>在Guava中，Stripe以抽象类的形式存在，它定义了通过给定key或index获得相应Lock/Semaphore/ReadWriteLock实例：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Striped&lt;L&gt;&nbsp;{<br />&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;*&nbsp;Returns&nbsp;the&nbsp;stripe&nbsp;that&nbsp;corresponds&nbsp;to&nbsp;the&nbsp;passed&nbsp;key.&nbsp;It&nbsp;is&nbsp;always&nbsp;guaranteed&nbsp;that&nbsp;if<br />&nbsp;&nbsp;&nbsp;*&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;key1.equals(key2)},&nbsp;then&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;get(key1)&nbsp;==&nbsp;get(key2)}.<br />&nbsp;&nbsp;&nbsp;*<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;key&nbsp;an&nbsp;arbitrary,&nbsp;non-null&nbsp;key<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;the&nbsp;stripe&nbsp;that&nbsp;the&nbsp;passed&nbsp;key&nbsp;corresponds&nbsp;to<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;L&nbsp;get(Object&nbsp;key);<br /><br />&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;*&nbsp;Returns&nbsp;the&nbsp;stripe&nbsp;at&nbsp;the&nbsp;specified&nbsp;index.&nbsp;Valid&nbsp;indexes&nbsp;are&nbsp;0,&nbsp;inclusively,&nbsp;to<br />&nbsp;&nbsp;&nbsp;*&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;size()},&nbsp;exclusively.<br />&nbsp;&nbsp;&nbsp;*<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;index&nbsp;the&nbsp;index&nbsp;of&nbsp;the&nbsp;stripe&nbsp;to&nbsp;return;&nbsp;must&nbsp;be&nbsp;in&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;[0<img src="http://www.blogjava.net/Images/dot.gif" alt="" />size())}<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;the&nbsp;stripe&nbsp;at&nbsp;the&nbsp;specified&nbsp;index<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;L&nbsp;getAt(<span style="color: #0000FF; ">int</span>&nbsp;index);<br /><br />&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;*&nbsp;Returns&nbsp;the&nbsp;index&nbsp;to&nbsp;which&nbsp;the&nbsp;given&nbsp;key&nbsp;is&nbsp;mapped,&nbsp;so&nbsp;that&nbsp;getAt(indexFor(key))&nbsp;==&nbsp;get(key).<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;indexFor(Object&nbsp;key);<br /><br />&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;*&nbsp;Returns&nbsp;the&nbsp;total&nbsp;number&nbsp;of&nbsp;stripes&nbsp;in&nbsp;this&nbsp;instance.<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;size();<br /><br />&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;*&nbsp;Returns&nbsp;the&nbsp;stripes&nbsp;that&nbsp;correspond&nbsp;to&nbsp;the&nbsp;passed&nbsp;objects,&nbsp;in&nbsp;ascending&nbsp;(as&nbsp;per<br />&nbsp;&nbsp;&nbsp;*&nbsp;{</span><span style="color: #808080; ">@link</span><span style="color: #008000; ">&nbsp;#getAt(int)})&nbsp;order.&nbsp;Thus,&nbsp;threads&nbsp;that&nbsp;use&nbsp;the&nbsp;stripes&nbsp;in&nbsp;the&nbsp;order&nbsp;returned<br />&nbsp;&nbsp;&nbsp;*&nbsp;by&nbsp;this&nbsp;method&nbsp;are&nbsp;guaranteed&nbsp;to&nbsp;not&nbsp;deadlock&nbsp;each&nbsp;other.<br />&nbsp;&nbsp;&nbsp;*<br />&nbsp;&nbsp;&nbsp;*&nbsp;&lt;p&gt;It&nbsp;should&nbsp;be&nbsp;noted&nbsp;that&nbsp;using&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;L&gt;}&nbsp;with&nbsp;relatively&nbsp;few&nbsp;stripes,&nbsp;and<br />&nbsp;&nbsp;&nbsp;*&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;bulkGet(keys)}&nbsp;with&nbsp;a&nbsp;relative&nbsp;large&nbsp;number&nbsp;of&nbsp;keys&nbsp;can&nbsp;cause&nbsp;an&nbsp;excessive&nbsp;number<br />&nbsp;&nbsp;&nbsp;*&nbsp;of&nbsp;shared&nbsp;stripes&nbsp;(much&nbsp;like&nbsp;the&nbsp;birthday&nbsp;paradox,&nbsp;where&nbsp;much&nbsp;fewer&nbsp;than&nbsp;anticipated&nbsp;birthdays<br />&nbsp;&nbsp;&nbsp;*&nbsp;are&nbsp;needed&nbsp;for&nbsp;a&nbsp;pair&nbsp;of&nbsp;them&nbsp;to&nbsp;match).&nbsp;Please&nbsp;consider&nbsp;carefully&nbsp;the&nbsp;implications&nbsp;of&nbsp;the<br />&nbsp;&nbsp;&nbsp;*&nbsp;number&nbsp;of&nbsp;stripes,&nbsp;the&nbsp;intended&nbsp;concurrency&nbsp;level,&nbsp;and&nbsp;the&nbsp;typical&nbsp;number&nbsp;of&nbsp;keys&nbsp;used&nbsp;in&nbsp;a<br />&nbsp;&nbsp;&nbsp;*&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;bulkGet(keys)}&nbsp;operation.&nbsp;See&nbsp;&lt;a&nbsp;href="</span><span style="color: #008000; text-decoration: underline; ">http://www.mathpages.com/home/kmath199.htm</span><span style="color: #008000; ">"&gt;Balls<br />&nbsp;&nbsp;&nbsp;*&nbsp;in&nbsp;Bins&nbsp;model&lt;/a&gt;&nbsp;for&nbsp;mathematical&nbsp;formulas&nbsp;that&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;estimate&nbsp;the&nbsp;probability&nbsp;of<br />&nbsp;&nbsp;&nbsp;*&nbsp;collisions.<br />&nbsp;&nbsp;&nbsp;*<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;keys&nbsp;arbitrary&nbsp;non-null&nbsp;keys<br />&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;the&nbsp;stripes&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;objects&nbsp;(one&nbsp;per&nbsp;each&nbsp;object,&nbsp;derived&nbsp;by&nbsp;delegating<br />&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;{</span><span style="color: #808080; ">@link</span><span style="color: #008000; ">&nbsp;#get(Object)};&nbsp;may&nbsp;contain&nbsp;duplicates),&nbsp;in&nbsp;an&nbsp;increasing&nbsp;index&nbsp;order.<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Iterable&lt;L&gt;&nbsp;bulkGet(Iterable&lt;?&gt;&nbsp;keys);<br />}</div>可以使用一下几个静态工厂方法创建相应的Striped实例，其中lazyWeakXXX创建的Striped实例中锁以弱引用的方式存在（在什么样的场景中使用呢？）：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Lock&gt;}&nbsp;with&nbsp;eagerly&nbsp;initialized,&nbsp;strongly&nbsp;referenced&nbsp;locks.<br />&nbsp;*&nbsp;Every&nbsp;lock&nbsp;is&nbsp;reentrant.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(locks)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Lock&gt;}<br />&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;Lock&gt;&nbsp;lock(<span style="color: #0000FF; ">int</span>&nbsp;stripes);<br /><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Lock&gt;}&nbsp;with&nbsp;lazily&nbsp;initialized,&nbsp;weakly&nbsp;referenced&nbsp;locks.<br />&nbsp;*&nbsp;Every&nbsp;lock&nbsp;is&nbsp;reentrant.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(locks)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Lock&gt;}<br />&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;Lock&gt;&nbsp;lazyWeakLock(<span style="color: #0000FF; ">int</span>&nbsp;stripes);<br /><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Semaphore&gt;}&nbsp;with&nbsp;eagerly&nbsp;initialized,&nbsp;strongly&nbsp;referenced&nbsp;semaphores,<br />&nbsp;*&nbsp;with&nbsp;the&nbsp;specified&nbsp;number&nbsp;of&nbsp;permits.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(semaphores)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;permits&nbsp;the&nbsp;number&nbsp;of&nbsp;permits&nbsp;in&nbsp;each&nbsp;semaphore<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Semaphore&gt;}<br />&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;Semaphore&gt;&nbsp;semaphore(<span style="color: #0000FF; ">int</span>&nbsp;stripes,&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;permits);<br /><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Semaphore&gt;}&nbsp;with&nbsp;lazily&nbsp;initialized,&nbsp;weakly&nbsp;referenced&nbsp;semaphores,<br />&nbsp;*&nbsp;with&nbsp;the&nbsp;specified&nbsp;number&nbsp;of&nbsp;permits.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(semaphores)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;permits&nbsp;the&nbsp;number&nbsp;of&nbsp;permits&nbsp;in&nbsp;each&nbsp;semaphore<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;Semaphore&gt;}<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;Semaphore&gt;&nbsp;lazyWeakSemaphore(<span style="color: #0000FF; ">int</span>&nbsp;stripes,&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;permits);<br /><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;ReadWriteLock&gt;}&nbsp;with&nbsp;eagerly&nbsp;initialized,&nbsp;strongly&nbsp;referenced<br />&nbsp;*&nbsp;read-write&nbsp;locks.&nbsp;Every&nbsp;lock&nbsp;is&nbsp;reentrant.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(locks)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;ReadWriteLock&gt;}<br />&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;ReadWriteLock&gt;&nbsp;readWriteLock(<span style="color: #0000FF; ">int</span>&nbsp;stripes);<br /><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;*&nbsp;Creates&nbsp;a&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;ReadWriteLock&gt;}&nbsp;with&nbsp;lazily&nbsp;initialized,&nbsp;weakly&nbsp;referenced<br />&nbsp;*&nbsp;read-write&nbsp;locks.&nbsp;Every&nbsp;lock&nbsp;is&nbsp;reentrant.<br />&nbsp;*<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@param</span><span style="color: #008000; ">&nbsp;stripes&nbsp;the&nbsp;minimum&nbsp;number&nbsp;of&nbsp;stripes&nbsp;(locks)&nbsp;required<br />&nbsp;*&nbsp;</span><span style="color: #808080; ">@return</span><span style="color: #008000; ">&nbsp;a&nbsp;new&nbsp;{</span><span style="color: #808080; ">@code</span><span style="color: #008000; ">&nbsp;Striped&lt;ReadWriteLock&gt;}<br />&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;Striped&lt;ReadWriteLock&gt;&nbsp;lazyWeakReadWriteLock(<span style="color: #0000FF; ">int</span>&nbsp;stripes);</div><br />Striped有两个具体实现类，CompactStriped和LazyStriped，他们都继承自PowerOfTwoStriped（用于表达内部保存的stripes值是2的指数值）。PowerOfTwoStriped实现了indexFor()方法，它使用hash值做映射函数：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;PowerOfTwoStriped&lt;L&gt;&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Striped&lt;L&gt;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; ">&nbsp;Capacity&nbsp;(power&nbsp;of&nbsp;two)&nbsp;minus&nbsp;one,&nbsp;for&nbsp;fast&nbsp;mod&nbsp;evaluation&nbsp;</span><span style="color: #008000; ">*/</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;mask;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;@Override&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;indexFor(Object&nbsp;key)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;hash&nbsp;=&nbsp;smear(key.hashCode());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;hash&nbsp;&amp;&nbsp;mask;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;smear(<span style="color: #0000FF; ">int</span>&nbsp;hashCode)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;hashCode&nbsp;^=&nbsp;(hashCode&nbsp;&gt;&gt;&gt;&nbsp;20)&nbsp;^&nbsp;(hashCode&nbsp;&gt;&gt;&gt;&nbsp;12);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;hashCode&nbsp;^&nbsp;(hashCode&nbsp;&gt;&gt;&gt;&nbsp;7)&nbsp;^&nbsp;(hashCode&nbsp;&gt;&gt;&gt;&nbsp;4);<br />&nbsp;&nbsp;}</div>CompactStriped类使用一个数组保存所有的Lock/Semaphore/ReadWriteLock实例，在初始化时就建立所有的锁实例；而LazyStriped类使用一个值为WeakReference的ConcurrentMap做为数据结构，index值为key，Lock/Semaphore/ReadWriteLock的WeakReference为值，所有锁实例在用到时动态创建。在CompactStriped中创建锁实例时对ReentrantLock/Semaphore创建采用PaddedXXX版本，不知道为何要做Pad。<br /><br />Striped类实现的类图如下：<br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/Striped_Class_Diagram.jpg" alt="" height="278" border="0" width="728" /><img src ="http://www.blogjava.net/DLevin/aggbug/407990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-12-25 10:03 <a href="http://www.blogjava.net/DLevin/archive/2013/12/25/407990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Cache系列之Guava Cache实现详解</title><link>http://www.blogjava.net/DLevin/archive/2013/10/20/404847.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 19 Oct 2013 16:17:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2013/10/20/404847.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/404847.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2013/10/20/404847.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/404847.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/404847.html</trackback:ping><description><![CDATA[Guava作为Google开源出来的工具库，Google自己对Guava的描述：The Guava project contains several of Google's core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.作为Google的core libraries，直接提供Cache实现，足以证明Cache应用的广泛程度。
然而作为工具库中的一部分，我们自然不能期待Guava对Cache有比较完善的实现。因而Guava中的Cache只能用于一些把Cache作为一种辅助设计的项目或者在项目的前期为了实现简单而引入。<br />
<br />
在Guava CacheBuilder的注释中给定Guava Cache以下的需求：
<ol style="margin-top:1px">
     <li>automatic loading of entries into the cache</li>
     <li>least-recently-used eviction when a maximum size is exceeded</li>
     <li>time-based expiration of entries, measured since last access or last write</li>
     <li>keys automatically wrapped in WeakReference</li>
     <li>values automatically wrapped in WeakReference or SoftReference soft</li>
     <li>notification of evicted (or otherwise removed) entries</li>
     <li>accumulation of cache access statistics</li>
</ol>
对于这样的需求，如果要我们自己来实现，我们应该怎么设计？对于我来说，对于其核心实现我会做如下的设计：
<ol style="margin-top:1px">
     <li>定义一个CacheConfig类用于纪录所有的配置，如CacheLoader，maximum size、expire time、key reference level、value reference level、eviction listener等。</li>
     <li>定义一个Cache接口，该接口类似Map（或ConcurrentMap），但是为了和Map区别开来，因而重新定义一个Cache接口。</li>
     <li>定义一个实现Cache接口的类CacheImpl，它接收CacheConfig作为参数的构造函数，并将CacheConfig实例保存在字段中。</li>
     <li>在实现上模仿ConcurrentHashMap的实现方式，有一个Segment数组，其长度由配置的concurrencyLevel值决定。为了实现最近最少使用算法（LRU），添加AccessQueue和WriteQueue字段，这两个Queue内部采用双链表，每次新创建一个Entry，就将这个Entry加入到这两个Queue的末尾，而每读取一个Entry就将其添加到AccessQueue的末尾，没更新一个Entry将该Entry添加到WriteQueue末尾。为了实现key和value上的WeakReference、SoftReference，添加ReferenceQueue&lt;K&gt;类型的keyReferenceQueue和valueReferenceQueue字段。</li>
     <li>在每次调用方法之前都遍历AccessQueue和WriteQueue，如果发现有Entry已经expire，就将该Entry从这两个Queue上和Cache中移除。然后遍历keyReferenceQueue和valueReference，如果发现有项存在，同样将它们移除。在移除时如果有EvictionListener注册着，则调用该listener。</li>
     <li>对Segment实现，它时一个CacheEntry数组，CacheEntry是一个链节点，它包含hash、key、vlaue、next。CacheEntry根据是否需要包装在WeakReference中创建WeakEntry或StrongEntry，而对value根据是否需要包装在WeakReference、SoftReference中创建WeakValueReference、SoftValueReference、StrongValueReference。在get操作中对于需要使用CacheLoader加载的值先添加一个具有LoadingValueReference值的Entry，这样可以保证同一个Key只加载依次。在加载成功后将LoadingValueReference根据配置替换成其他Weak、Soft、Strong ValueReference。<br />
     </li>
     <li>对于cache access statistics，只需要有一个类在需要的地方做一些统计计数即可。</li>
     <li>最后我必须得承认以上的设计有很多是对Guava Cache的参考，我有点后悔没有在看源码之前考虑这个问题，等看过以后思路就被它的实现给羁绊了。。。。</li>
</ol>
<strong>
Guava Cache的数据结构</strong><br />
因为新进一家公司，要熟悉新公司项目以及项目用到的第三方库的代码，因而几个月来看了许多代码。然后越来越发现要理解一个项目的最快方法是先搞清楚该项目的底层数据结构，然后再去看构建于这些数据结构以上的逻辑就会容易许多。记得在还是学生的时候，有在一本书上看到过一个大牛说的一句话：程序＝数据结构＋算法；当时对这句话并不是和理解，现在是很赞同这句话，我对算法接触的不多，因而我更倾向于将这里的算法理解长控制数据流动的逻辑。因而我们先来熟悉一下Guava Cache的数据结构。<br />
<br />
Cache类似于Map，它是存储键值对的集合，然而它和Map不同的是它还需要处理evict、expire、dynamic load等逻辑，需要一些额外信息来实现这些操作。在面向对象思想中，经常使用类对一些关联性比较强的数据做封装，同时把操作这些数据相关的操作放到该类中。因而Guava Cache使用ReferenceEntry接口来封装一个键值对，而用ValueReference来封装Value值。这里之所以用Reference命令，是因为Guava Cache要支持WeakReference Key和SoftReference、WeakReference value。<br />
<br />
<strong>ValueReference</strong><br />
对于ValueReference，因为Guava Cache支持强引用的Value、SoftReference Value以及WeakReference Value，因而它对应三个实现类：StrongValueReference、SoftValueReference、WeakValueReference。为了支持动态加载机制，它还有一个LoadingValueReference，在需要动态加载一个key的值时，先把该值封装在LoadingValueReference中，以表达该key对应的值已经在加载了，如果其他线程也要查询该key对应的值，就能得到该引用，并且等待改值加载完成，从而保证该值只被加载一次（可以在evict以后重新加载）。在该只加载完成后，将LoadingValueReference替换成其他ValueReference类型。对新创建的LoadingValueReference，由于其内部oldValue的初始值是UNSET，它isActive为false，isLoading为false，因而此时的LoadingValueReference的isActive为false，但是isLoading为true。每个ValueReference都纪录了weight值，所谓weight从字面上理解是&#8220;该值的重量&#8221;，它由Weighter接口计算而得。weight在Guava Cache中由两个用途：1. 对weight值为0时，在计算因为size limit而evict是忽略该Entry（它可以通过其他机制evict）；2. 如果设置了maximumWeight值，则当Cache中weight和超过了该值时，就会引起evict操作。但是目前还不知道这个设计的用途。最后，Guava Cache还定义了Stength枚举类型作为ValueReference的factory类，它有三个枚举值：Strong、Soft、Weak，这三个枚举值分别创建各自的ValueReference，并且根据传入的weight值是否为1而决定是否要创建Weight版本的ValueReference。以下是ValueReference的类图： <br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/guava_cache_valuereference.png" alt="" height="748" border="0" width="994" />
<br />这里ValueReference之所以要有对ReferenceEntry的引用是因为在Value因为WeakReference、SoftReference被回收时，需要使用其key将对应的项从Segment的table中移除；copyFor()函数的存在是因为在expand(rehash)重新创建节点时，对WeakReference、SoftReference需要重新创建实例（个人感觉是为了保持对象状态不会相互影响，但是不确定是否还有其他原因），而对强引用来说，直接使用原来的值即可，这里很好的展示了对彼变化的封装思想；notifiyNewValue只用于LoadingValueReference，它的存在是为了对LoadingValueReference来说能更加及时的得到CacheLoader加载的值。<br /><br /><strong>ReferenceEntry</strong><br />
ReferenceEntry是Guava Cache中对一个键值对节点的抽象。和ConcurrentHashMap一样，Guava Cache由多个Segment组成，而每个Segment包含一个ReferenceEntry数组，每个ReferenceEntry数组项都是一条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段。除了在ReferenceEntry数组项中组成的链，在一个Segment中，所有ReferenceEntry还组成access链（accessQueue）和write链（writeQueue），这两条都是双向链表，分别通过previousAccess、nextAccess和previousWrite、nextWrite字段链接而成。在对每个节点的更新操作都会将该节点重新链到write链和access链末尾，并且更新其writeTime和accessTime字段，而没找到一个节点，都会将该节点重新链到access链末尾，并更新其accessTime字段。这两个双向链表的存在都是为了实现采用最近最少使用算法（LRU）的evict操作（expire、size limit引起的evict）。<br />
<br />
Guava Cache中的ReferenceEntry可以是强引用类型的key，也可以WeakReference类型的key，为了减少内存使用量，还可以根据是否配置了expireAfterWrite、expireAfterAccess、maximumSize来决定是否需要write链和access链确定要创建的具体Reference：StrongEntry、StrongWriteEntry、StrongAccessEntry、StrongWriteAccessEntry等。创建不同类型的ReferenceEntry由其枚举工厂类EntryFactory来实现，它根据key的Strongth类型、是否使用accessQueue、是否使用writeQueue来决定不同的EntryFactry实例，并通过它创建相应的ReferenceEntry实例。ReferenceEntry类图如下： <br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/guava_cache_referenceentry.png" alt="" height="1202" border="0" width="1219" />
<br />
<strong>
WriteQueue和AccessQueue
</strong><br />
为了实现最近最少使用算法，Guava Cache在Segment中添加了两条链：write链（writeQueue）和access链（accessQueue），这两条链都是一个双向链表，通过ReferenceEntry中的previousInWriteQueue、nextInWriteQueue和previousInAccessQueue、nextInAccessQueue链接而成，但是以Queue的形式表达。WriteQueue和AccessQueue都是自定义了offer、add（直接调用offer）、remove、poll等操作的逻辑，对于offer（add）操作，如果是新加的节点，则直接加入到该链的结尾，如果是已存在的节点，则将该节点链接的链尾；对remove操作，直接从该链中移除该节点；对poll操作，将头节点的下一个节点移除，并返回。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;WriteQueue&lt;K,&nbsp;V&gt;&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;AbstractQueue&lt;ReferenceEntry&lt;K,&nbsp;V&gt;&gt;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;head&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;AbstractReferenceEntry&lt;K,&nbsp;V&gt;() ....<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;offer(ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;entry)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;unlink</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connectWriteOrder(entry.getPreviousInWriteQueue(),&nbsp;entry.getNextInWriteQueue());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;add&nbsp;to&nbsp;tail</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connectWriteOrder(head.getPreviousInWriteQueue(),&nbsp;entry);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connectWriteOrder(entry,&nbsp;head);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">true</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;peek()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;next&nbsp;=&nbsp;head.getNextInWriteQueue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;(next&nbsp;==&nbsp;head)&nbsp;?&nbsp;<span style="color: #0000FF; ">null</span>&nbsp;:&nbsp;next;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;poll()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;next&nbsp;=&nbsp;head.getNextInWriteQueue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(next&nbsp;==&nbsp;head)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(next);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;next;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;remove(Object&nbsp;o)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;e&nbsp;=&nbsp;(ReferenceEntry)&nbsp;o;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;previous&nbsp;=&nbsp;e.getPreviousInWriteQueue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;next&nbsp;=&nbsp;e.getNextInWriteQueue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connectWriteOrder(previous,&nbsp;next);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nullifyWriteOrder(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;next&nbsp;!=&nbsp;NullEntry.INSTANCE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;contains(Object&nbsp;o)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;e&nbsp;=&nbsp;(ReferenceEntry)&nbsp;o;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;e.getNextInWriteQueue()&nbsp;!=&nbsp;NullEntry.INSTANCE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
....<br />
&nbsp;&nbsp;}</div>
<br />
对于不需要维护WriteQueue和AccessQueue的配置（即没有expire time或size limit的evict策略）来说，我们可以使用DISCARDING_QUEUE以节省内存：
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Queue&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Object&gt;&nbsp;DISCARDING_QUEUE&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;AbstractQueue&lt;Object&gt;()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;offer(Object&nbsp;o)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">true</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;peek()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;poll()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
....<br />
&nbsp;&nbsp;};</div><br />
<br /><strong>
Segment中的evict</strong><br />在解决了所有数据结构的问题以后，让我们来看看LocalCache中的核心类Segment的实现，首先从evict开始。在Guava Cache的evict时机上，它没有使用另一个后台线程每隔一段时间扫瞄一次table以evict那些已经expire的entry。而是它在每次操作开始和结束时才做一遍清理工作，这样可以减少开销，但是如果长时间不调用方法的话，会引起有些entry不能及时被evict出去。evict主要处理四个Queue：1. keyReferenceQueue；2. valueReferenceQueue；3. writeQueue；4. accessQueue。前两个queue是因为WeakReference、SoftReference被垃圾回收时加入的，清理时只需要遍历整个queue，将对应的项从LocalCache中移除即可，这里keyReferenceQueue存放ReferenceEntry，而valueReferenceQueue存放的是ValueReference，要从LocalCache中移除需要有key，因而ValueReference需要有对ReferenceEntry的引用。这里的移除通过LocalCache而不是Segment是因为在移除时因为expand（rehash）可能导致原来在某个Segment中的ReferenceEntry后来被移动到另一个Segment中了。而对后两个Queue，只需要检查是否配置了相应的expire时间，然后从头开始查找已经expire的Entry，将它们移除即可。有不同的是在移除时，还会注册移除的事件，这些事件将会在接下来的操作调用注册的RemovalListener触发，这些代码比较简单，不详述。<br />在put的时候，还会清理recencyQueue，即将recencyQueue中的Entry添加到accessEntry中，此时可能会发生某个Entry实际上已经被移除了，但是又被添加回accessQueue中了，这种情况下，如果没有使用WeakReference、SoftReference，也没有配置expire时间，则会引起一些内存泄漏问题。recencyQueue在get操作时被添加，但是为什么会有这个Queue的存在一直没有想明白。<br /><br /><strong>Segment中的put操作</strong><br />put操作相对比较简单，首先它需要获得锁，然后尝试做一些清理工作，接下来的逻辑类似ConcurrentHashMap中的rehash，不详述。需要说明的是当找到一个已存在的Entry时，需要先判断当前的ValueRefernece中的值事实上已经被回收了，因为它们可以时WeakReference、SoftReference类型，如果已经被回收了，则将新值写入。并且在每次更新时注册当前操作引起的移除事件，指定相应的原因：COLLECTED、REPLACED等，这些注册的事件在退出的时候统一调用LocalCache注册的RemovalListener，由于事件处理可能会有很长时间，因而这里将事件处理的逻辑在退出锁以后才做。最后，在更新已存在的Entry结束后都尝试着将那些已经expire的Entry移除。另外put操作中还需要更新writeQueue和accessQueue的语义正确性。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp; V&nbsp;put(K&nbsp;key,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;hash,&nbsp;V&nbsp;value,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;onlyIfAbsent)&nbsp;{<br />&nbsp; &nbsp; &nbsp; ....<span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">for</span>&nbsp;(ReferenceEntry&lt;K,&nbsp;V&gt;&nbsp;e&nbsp;=&nbsp;first;&nbsp;e&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>;&nbsp;e&nbsp;=&nbsp;e.getNext())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;entryKey&nbsp;=&nbsp;e.getKey();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(e.getHash()&nbsp;==&nbsp;hash&nbsp;&amp;&amp;&nbsp;entryKey&nbsp;!=&nbsp;<span style="color: #0000FF; ">null&nbsp;</span>&amp;&amp;&nbsp;map.keyEquivalence.equivalent(key,&nbsp;entryKey))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ValueReference&lt;K,&nbsp;V&gt;&nbsp;valueReference&nbsp;=&nbsp;e.getValueReference();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;V&nbsp;entryValue&nbsp;=&nbsp;valueReference.get();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(entryValue&nbsp;==&nbsp;<span style="color: #0000FF; ">null</span>)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++modCount;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(valueReference.isActive())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enqueueNotification(key,&nbsp;hash,&nbsp;valueReference,&nbsp;RemovalCause.COLLECTED);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setValue(e,&nbsp;key,&nbsp;value,&nbsp;now);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newCount&nbsp;=&nbsp;<span style="color: #0000FF; ">this</span>.count;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;count&nbsp;remains&nbsp;unchanged</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setValue(e,&nbsp;key,&nbsp;value,&nbsp;now);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newCount&nbsp;=&nbsp;<span style="color: #0000FF; ">this</span>.count&nbsp;+&nbsp;1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.count&nbsp;=&nbsp;newCount;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;write-volatile</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evictEntries();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">null</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(onlyIfAbsent)&nbsp;{<span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;recordLockedRead(e,&nbsp;now);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;entryValue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;{<span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++modCount;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enqueueNotification(key,&nbsp;hash,&nbsp;valueReference,&nbsp;RemovalCause.REPLACED);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setValue(e,&nbsp;key,&nbsp;value,&nbsp;now);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;evictEntries();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;entryValue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;postWriteCleanup();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><br /><strong>Segment带CacheLoader的get操作</strong><br />这部分的代码有点不知道怎么说了，大概上的步骤是：1. 先查找table中是否已存在没有被回收、也没有expire的entry，如果找到，并在CacheBuilder中配置了refreshAfterWrite，并且当前时间间隔已经操作这个事件，则重新加载值，否则，直接返回原有的值；2. 如果查找到的ValueReference是LoadingValueReference，则等待该LoadingValueReference加载结束，并返回加载的值；3. 如果没有找到entry，或者找到的entry的值为null，则加锁后，继续table中已存在key对应的entry，如果找到并且对应的entry.isLoading()为true，则表示有另一个线程正在加载，因而等待那个线程加载完成，如果找到一个非null值，返回该值，否则创建一个LoadingValueReference，并调用loadSync加载相应的值，在加载完成后，将新加载的值更新到table中，即大部分情况下替换原来的LoadingValueReference。<br /><br /><strong>Segment中的其他操作</strong><br />其他操作包括不含CacheLoader的get、containsKey、containsValue、replace等操作逻辑重复性很大，而且和ConcurrentHashMap的实现方式也类似，不在详述。<br /><br /><strong>Cache StatsCounter和CacheStats</strong><br />为了纪录Cache的使用情况，如果命中次数、没有命中次数、evict次数等，Guava Cache中定义了StatsCounter做这些统计信息，它有一个简单的SimpleStatsCounter实现，我们也可以通过CacheBuilder配置自己的StatsCounter。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;StatsCounter&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;recordHits(<span style="color: #0000FF; ">int</span>&nbsp;count);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;recordMisses(<span style="color: #0000FF; ">int</span>&nbsp;count);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;recordLoadSuccess(<span style="color: #0000FF; ">long</span>&nbsp;loadTime);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;recordLoadException(<span style="color: #0000FF; ">long</span>&nbsp;loadTime);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;recordEviction();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;CacheStats&nbsp;snapshot();<br />&nbsp;&nbsp;}</div>在得到StatsCounter实例后，可以使用CacheStats获取具体的统计信息：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;CacheStats&nbsp;{<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;hitCount;<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;missCount;<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;loadSuccessCount;<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;loadExceptionCount;<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;totalLoadTime;<br />&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;evictionCount;<br /><img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />}</div><br />同ConcurrentHashMap，在知道Segment实现以后，其他的方法基本上都是代理给Segment内部方法，因而在LocalCache类中的其他方法看起来就比较容易理解，不在详述。然而Guava Cache并没有将ConcurrentMap直接提供给用户使用，而是为了区分Cache和Map，它自定义了一个自己的Cache接口和LoadingCache接口，我们可以通过CacheBuilder配置不同的参数，然后使用build()方法返回一个Cache或LoadingCache实例：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Cache&lt;K,&nbsp;V&gt;&nbsp;{<br />&nbsp;&nbsp;V&nbsp;getIfPresent(Object&nbsp;key);<br />&nbsp;&nbsp;V&nbsp;get(K&nbsp;key,&nbsp;Callable&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;valueLoader)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ExecutionException;<br />&nbsp;&nbsp;ImmutableMap&lt;K,&nbsp;V&gt;&nbsp;getAllPresent(Iterable&lt;?&gt;&nbsp;keys);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;putAll(Map&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K,?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;V&gt;&nbsp;m);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidate(Object&nbsp;key);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll(Iterable&lt;?&gt;&nbsp;keys);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll();<br />&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;size();<br />&nbsp;&nbsp;CacheStats&nbsp;stats();<br />&nbsp;&nbsp;ConcurrentMap&lt;K,&nbsp;V&gt;&nbsp;asMap();<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;cleanUp();<br />}<br /><br /><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;LoadingCache&lt;K,&nbsp;V&gt;&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Cache&lt;K,&nbsp;V&gt;,&nbsp;Function&lt;K,&nbsp;V&gt;&nbsp;{<br />&nbsp;&nbsp;V&nbsp;get(K&nbsp;key)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ExecutionException;<br />&nbsp;&nbsp;V&nbsp;getUnchecked(K&nbsp;key);<br />&nbsp;&nbsp;ImmutableMap&lt;K,&nbsp;V&gt;&nbsp;getAll(Iterable&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;K&gt;&nbsp;keys)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ExecutionException;<br />&nbsp;&nbsp;V&nbsp;apply(K&nbsp;key);<br />&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;refresh(K&nbsp;key);<br />&nbsp;&nbsp;ConcurrentMap&lt;K,&nbsp;V&gt;&nbsp;asMap();<br />}</div><img src ="http://www.blogjava.net/DLevin/aggbug/404847.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2013-10-20 00:17 <a href="http://www.blogjava.net/DLevin/archive/2013/10/20/404847.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>