欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實(shí)現(xiàn)LRU緩存的實(shí)例詳解

 更新時(shí)間:2017年08月14日 11:42:14   投稿:lqh  
這篇文章主要介紹了Java實(shí)現(xiàn)LRU緩存的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例幫助大家理解掌握這部分內(nèi)容,需要的朋友可以參考下

Java實(shí)現(xiàn)LRU緩存的實(shí)例詳解

1.Cache

Cache對(duì)于代碼系統(tǒng)的加速與優(yōu)化具有極大的作用,對(duì)于碼農(nóng)來(lái)說(shuō)是一個(gè)很熟悉的概念??梢哉f(shuō),你在內(nèi)存中new 了一個(gè)一段空間(比方說(shuō)數(shù)組,list)存放一些冗余的結(jié)果數(shù)據(jù),并利用這些數(shù)據(jù)完成了以空間換時(shí)間的優(yōu)化目的,你就已經(jīng)使用了cache。

有服務(wù)級(jí)的緩存框架,如memcache,Redis等。其實(shí),很多時(shí)候,我們?cè)谧约和粋€(gè)服務(wù)內(nèi),或者單個(gè)進(jìn)程內(nèi)也需要緩存,例如,lucene就對(duì)搜索做了緩存,而無(wú)須依賴外界。那么,我們?nèi)绾螌?shí)現(xiàn)我們自己的緩存?還要帶自動(dòng)失效的,最好還是LRU(Least Recently Used)。

當(dāng)你思考怎么去實(shí)現(xiàn),你可能會(huì)想得很遠(yuǎn)。為了LRU,需要把剛使用的數(shù)據(jù)存入棧,或者紀(jì)錄每個(gè)數(shù)據(jù)最近使用的時(shí)間,再來(lái)的定時(shí)掃描失效的線程….其實(shí),Java本身就已經(jīng)為我們提供了LRU Cache很好的實(shí)現(xiàn),即LinkedHashMap。

2.LinkedHashMap分析

很多沒(méi)有去細(xì)究過(guò)其內(nèi)部實(shí)現(xiàn)的人,只是將其當(dāng)作一個(gè)普通的hashMap來(lái)對(duì)待。LinkedHashMap是一個(gè)雙向鏈表,加上HashTable的實(shí)現(xiàn)。表現(xiàn)出來(lái)與普通HashMap的一個(gè)區(qū)別就是LinkedHashMap會(huì)記錄存入其中的數(shù)據(jù)的順序,并能按順取出。
為了實(shí)現(xiàn),一個(gè)hash表,自然應(yīng)該先申請(qǐng)?jiān)谝黄B續(xù)的內(nèi)存空間上。當(dāng)需要存入數(shù)據(jù)的時(shí)候,根據(jù)相應(yīng)的hash值存入。而LinkedHashMap在這個(gè)基礎(chǔ)上,為每個(gè)entry設(shè)置了before與after屬性,形了一個(gè)雙向鏈表,記錄了他們put進(jìn)入的前后順序。

不僅如此,每當(dāng)通過(guò)get來(lái)獲得某個(gè)元素后,get方法內(nèi)部,會(huì)在最后通過(guò)afterNodeAccess方法來(lái)調(diào)整鏈表的指向:

void afterNodeAccess(Node<K,V> e) { // move node to last
  LinkedHashMap.Entry<K,V> last;
  if (accessOrder && (last = tail) != e) {
    LinkedHashMap.Entry<K,V> p =
      (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
    p.after = null;
    if (b == null)
      head = a;
    else
      b.after = a;
    if (a != null)
      a.before = b;
    else
      last = b;
    if (last == null)
      head = p;
    else {
      p.before = last;
      last.after = p;
    }
    tail = p;
    ++modCount;
  }
}

上述代碼將Node e移至了雙向鏈表的未尾。而在方法afterNodeInsertion中,只要滿足條件,便移除最老的數(shù)據(jù),即鏈表的head。

void afterNodeInsertion(boolean evict) { // possibly remove eldest
  LinkedHashMap.Entry<K,V> first;
  if (evict && (first = head) != null && removeEldestEntry(first)) {
    K key = first.key;
    removeNode(hash(key), key, null, false, true);
  }
} 

可見(jiàn),當(dāng)你為L(zhǎng)inkedHashMap設(shè)置有限空間的時(shí)候,自然便完成了LRU Cache的效果。當(dāng)然還有一個(gè)前提,你必須重寫一個(gè)方法removeEldestEntry,返回true。表示空間已滿時(shí),刪除最老的。

@Override
public boolean removeEldestEntry(Map.Entry<K, V> eldest){   
  return size()>capacity;    
}

3.線程安全的LRU Cache

如此,我們就獲得了一個(gè)LRU緩存利器,滿足了我們大多場(chǎng)景下的需求。但還有一個(gè)問(wèn)題,它不是線程安全的。在多線程的情況下,你有可能需要對(duì)某些Cache做同步處理。這時(shí)候,你再找,可以看到j(luò)ava有ConcurrentHashMap的實(shí)現(xiàn),但并不存在ConcurrentLinkedHashMap這樣的類。

當(dāng)然這個(gè)問(wèn)題也不大,我們可以對(duì)再有的LinkedHashMap,再作封裝,對(duì)get,put, 之類的方法加上同步操作。

目前,我們所用的處理,是直接采和google提供的guava包,這里面就提供了我們想要的ConcurrentLinkedHashMap。這樣就可以很方便地實(shí)現(xiàn)一個(gè)線程安全。具體代碼如下:

import java.util.Set;

 import com.googlecode.concurrentlinkedhashmap.Weighers;
 import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
 public class ConcurrentLRUCache<K, V> {
   public static final int           DEFAULT_CONCURENCY_LEVEL = 32;

 private final ConcurrentLinkedHashMap<K, V> map;


 public ConcurrentLRUCache(int capacity) {
   this(capacity, DEFAULT_CONCURENCY_LEVEL);
 }

 public ConcurrentLRUCache(int capacity, int concurrency) {
   map = new ConcurrentLinkedHashMap.Builder<K, V>().weigher(Weighers.<V> singleton())
    .initialCapacity(capacity).maximumWeightedCapacity(capacity)
    .concurrencyLevel(concurrency).build();
 }

 public void put(K key, V value) {
   map.put(key, value);
 }

 public V get(K key) {
   V v = map.get(key);
   return v;
 }

 public V getInternal(K key) {
   return map.get(key);
 }

 public void remove(K key) {
   map.remove(key);
 }

 public long getCapacity() {
   return map.capacity();
 }


 public void updateCapacity(int capacity) {
   map.setCapacity(capacity);
 }


 public int getSize() {
   return map.size();
 }


 public void clear() {
   map.clear();
 }

 public Set<K> getKeySet() {
   return map.keySet();
 }
}

以上就是Java實(shí)現(xiàn)LRU緩存的實(shí)例,如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • 基于Java的Socket編寫的C/S聊天程序?qū)崿F(xiàn)

    基于Java的Socket編寫的C/S聊天程序?qū)崿F(xiàn)

    這篇文章主要介紹了基于Java的Socket編寫的C/S聊天程序?qū)崿F(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • java批量修改文件后綴名方法總結(jié)

    java批量修改文件后綴名方法總結(jié)

    在本篇文章里小編給大家分享了關(guān)于java批量修改文件后綴名方法和相關(guān)知識(shí)點(diǎn),有需要的朋友們學(xué)習(xí)下。
    2019-03-03
  • Spark Streaming編程初級(jí)實(shí)踐詳解

    Spark Streaming編程初級(jí)實(shí)踐詳解

    這篇文章主要為大家介紹了Spark Streaming編程初級(jí)實(shí)踐詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Springboot引用外部配置文件的方法步驟

    Springboot引用外部配置文件的方法步驟

    這篇文章主要介紹了Springboot引用外部配置文件的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • java實(shí)現(xiàn)String字符串處理各種類型轉(zhuǎn)換

    java實(shí)現(xiàn)String字符串處理各種類型轉(zhuǎn)換

    在日常的程序開(kāi)發(fā)中,經(jīng)常會(huì)涉及到不同類型之間的轉(zhuǎn)換,本文主要介紹了String字符串處理各種類型轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Java多線程之如何確定線程數(shù)的方法

    Java多線程之如何確定線程數(shù)的方法

    創(chuàng)建線程和銷毀線程都是比較耗時(shí)的操作,如果每個(gè)任務(wù)都創(chuàng)建一個(gè)線程去處理,這樣線程會(huì)越來(lái)越多,那么應(yīng)該如何確定線程的數(shù)量,本文就詳細(xì)的介紹一下,感興趣的可以了解一下
    2022-03-03
  • Java教程各種接口的介紹

    Java教程各種接口的介紹

    Java教程各種接口的介紹,需要的朋友可以參考一下
    2013-02-02
  • Java中使用Lambda表達(dá)式和函數(shù)編程示例

    Java中使用Lambda表達(dá)式和函數(shù)編程示例

    這篇文章介紹了Java中使用Lambda表達(dá)式和函數(shù)編程示例,該文章會(huì)演示多個(gè)示列,分別是變量聲明上下文中的lambda、return語(yǔ)句上下文中的lambda、賦值上下文中的lambda、lambda在數(shù)組初始值設(shè)定項(xiàng)上下文中的用法等等,需要的朋友可以參考一下
    2021-10-10
  • SpringMVC使用RESTful接口案例詳解

    SpringMVC使用RESTful接口案例詳解

    RESTful是一種web軟件風(fēng)格,它不是標(biāo)準(zhǔn)也不是協(xié)議,它不一定要采用,只是一種風(fēng)格,它倡導(dǎo)的是一個(gè)資源定位(url)及資源操作的風(fēng)格,這篇文章主要介紹了SpringBoot使用RESTful接口
    2022-11-11
  • 微信跳一跳刷分java代碼實(shí)現(xiàn)

    微信跳一跳刷分java代碼實(shí)現(xiàn)

    這篇文章主要介紹了微信跳一跳刷分java代碼實(shí)現(xiàn),還為大家分享了java刷微信跳一跳問(wèn)題集,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01

最新評(píng)論