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

詳解Java實現(xiàn)緩存(LRU,FIFO)

 更新時間:2017年04月05日 08:30:26   作者:liuyang0  
本篇文章主要介紹了詳解Java實現(xiàn)緩存(LRU,FIFO) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

現(xiàn)在軟件或者網(wǎng)頁的并發(fā)量越來越大了,大量請求直接操作數(shù)據(jù)庫會對數(shù)據(jù)庫造成很大的壓力,處理大量連接和請求就會需要很長時間,但是實際中百分之80的數(shù)據(jù)是很少更改的,這樣就可以引入緩存來進行讀取,減少數(shù)據(jù)庫的壓力。

常用的緩存有Redis和memcached,但是有時候一些小場景就可以直接使用Java實現(xiàn)緩存,就可以滿足這部分服務的需求。

緩存主要有LRU和FIFO,LRU是Least Recently Used的縮寫,即最近最久未使用,F(xiàn)IFO就是先進先出,下面就使用Java來實現(xiàn)這兩種緩存。

LRU

LRU緩存的思想

  • 固定緩存大小,需要給緩存分配一個固定的大小。
  • 每次讀取緩存都會改變緩存的使用時間,將緩存的存在時間重新刷新。
  • 需要在緩存滿了后,將最近最久未使用的緩存刪除,再添加最新的緩存。

按照如上思想,可以使用LinkedHashMap來實現(xiàn)LRU緩存。

這是LinkedHashMap的一個構造函數(shù),傳入的第三個參數(shù)accessOrder為true的時候,就按訪問順序?qū)inkedHashMap排序,為false的時候就按插入順序,默認是為false的。

當把accessOrder設置為true后,就可以將最近訪問的元素置于最前面,這樣就可以滿足上述的第二點。

/**
 * Constructs an empty <tt>LinkedHashMap</tt> instance with the
 * specified initial capacity, load factor and ordering mode.
 *
 * @param initialCapacity the initial capacity
 * @param loadFactor   the load factor
 * @param accessOrder   the ordering mode - <tt>true</tt> for
 *     access-order, <tt>false</tt> for insertion-order
 * @throws IllegalArgumentException if the initial capacity is negative
 *     or the load factor is nonpositive
 */
public LinkedHashMap(int initialCapacity,
           float loadFactor,
           boolean accessOrder) {
  super(initialCapacity, loadFactor);
  this.accessOrder = accessOrder;
}

這是LinkedHashMap中另外一個方法,當返回true的時候,就會remove其中最久的元素,可以通過重寫這個方法來控制緩存元素的刪除,當緩存滿了后,就可以通過返回true刪除最久未被使用的元素,達到LRU的要求。這樣就可以滿足上述第三點要求。

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
  return false;
}

由于LinkedHashMap是為自動擴容的,當table數(shù)組中元素大于Capacity * loadFactor的時候,就會自動進行兩倍擴容。但是為了使緩存大小固定,就需要在初始化的時候傳入容量大小和負載因子。

 為了使得到達設置緩存大小不會進行自動擴容,需要將初始化的大小進行計算再傳入,可以將初始化大小設置為(緩存大小 / loadFactor) + 1,這樣就可以在元素數(shù)目達到緩存大小時,也不會進行擴容了。這樣就解決了上述第一點問題。

通過上面分析,實現(xiàn)下面的代碼

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class LRU1<K, V> {
  private final int MAX_CACHE_SIZE;
  private final float DEFAULT_LOAD_FACTORY = 0.75f;

  LinkedHashMap<K, V> map;

  public LRU1(int cacheSize) {
    MAX_CACHE_SIZE = cacheSize;
    int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1;
    /*
     * 第三個參數(shù)設置為true,代表linkedlist按訪問順序排序,可作為LRU緩存
     * 第三個參數(shù)設置為false,代表按插入順序排序,可作為FIFO緩存
     */
    map = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, true) {
      @Override
      protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > MAX_CACHE_SIZE;
      }
    };
  }

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

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

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

  public synchronized Set<Map.Entry<K, V>> getAll() {
    return map.entrySet();
  }

  @Override
  public String toString() {
    StringBuilder stringBuilder = new StringBuilder();
    for (Map.Entry<K, V> entry : map.entrySet()) {
      stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue()));
    }
    return stringBuilder.toString();
  }

  public static void main(String[] args) {
    LRU1<Integer, Integer> lru1 = new LRU1<>(5);
    lru1.put(1, 1);
    lru1.put(2, 2);
    lru1.put(3, 3);
    System.out.println(lru1);
    lru1.get(1);
    System.out.println(lru1);
    lru1.put(4, 4);
    lru1.put(5, 5);
    lru1.put(6, 6);
    System.out.println(lru1);
  }
}

運行結果:

從運行結果中可以看出,實現(xiàn)了LRU緩存的思想。

接著使用HashMap和鏈表來實現(xiàn)LRU緩存。

主要的思想和上述基本一致,每次添加元素或者讀取元素就將元素放置在鏈表的頭,當緩存滿了之后,就可以將尾結點元素刪除,這樣就實現(xiàn)了LRU緩存。

這種方法中是通過自己編寫代碼移動結點和刪除結點,為了防止緩存大小超過限制,每次進行put的時候都會進行檢查,若緩存滿了則刪除尾部元素。

import java.util.HashMap;

/**
 * 使用cache和鏈表實現(xiàn)緩存
 */
public class LRU2<K, V> {
  private final int MAX_CACHE_SIZE;
  private Entry<K, V> head;
  private Entry<K, V> tail;

  private HashMap<K, Entry<K, V>> cache;

  public LRU2(int cacheSize) {
    MAX_CACHE_SIZE = cacheSize;
    cache = new HashMap<>();
  }

  public void put(K key, V value) {
    Entry<K, V> entry = getEntry(key);
    if (entry == null) {
      if (cache.size() >= MAX_CACHE_SIZE) {
        cache.remove(tail.key);
        removeTail();
      }
    }
    entry = new Entry<>();
    entry.key = key;
    entry.value = value;
    moveToHead(entry);
    cache.put(key, entry);
  }

  public V get(K key) {
    Entry<K, V> entry = getEntry(key);
    if (entry == null) {
      return null;
    }
    moveToHead(entry);
    return entry.value;
  }

  public void remove(K key) {
    Entry<K, V> entry = getEntry(key);
    if (entry != null) {
      if (entry == head) {
        Entry<K, V> next = head.next;
        head.next = null;
        head = next;
        head.pre = null;
      } else if (entry == tail) {
        Entry<K, V> prev = tail.pre;
        tail.pre = null;
        tail = prev;
        tail.next = null;
      } else {
        entry.pre.next = entry.next;
        entry.next.pre = entry.pre;
      }
      cache.remove(key);
    }
  }

  private void removeTail() {
    if (tail != null) {
      Entry<K, V> prev = tail.pre;
      if (prev == null) {
        head = null;
        tail = null;
      } else {
        tail.pre = null;
        tail = prev;
        tail.next = null;
      }
    }
  }

  private void moveToHead(Entry<K, V> entry) {
    if (entry == head) {
      return;
    }
    if (entry.pre != null) {
      entry.pre.next = entry.next;
    }
    if (entry.next != null) {
      entry.next.pre = entry.pre;
    }
    if (entry == tail) {
      Entry<K, V> prev = entry.pre;
      if (prev != null) {
        tail.pre = null;
        tail = prev;
        tail.next = null;
      }
    }

    if (head == null || tail == null) {
      head = tail = entry;
      return;
    }

    entry.next = head;
    head.pre = entry;
    entry.pre = null;
    head = entry;
  }

  private Entry<K, V> getEntry(K key) {
    return cache.get(key);
  }

  private static class Entry<K, V> {
    Entry<K, V> pre;
    Entry<K, V> next;
    K key;
    V value;
  }

  @Override
  public String toString() {
    StringBuilder stringBuilder = new StringBuilder();
    Entry<K, V> entry = head;
    while (entry != null) {
      stringBuilder.append(String.format("%s:%s ", entry.key, entry.value));
      entry = entry.next;
    }
    return stringBuilder.toString();
  }

  public static void main(String[] args) {
    LRU2<Integer, Integer> lru2 = new LRU2<>(5);
    lru2.put(1, 1);
    System.out.println(lru2);
    lru2.put(2, 2);
    System.out.println(lru2);
    lru2.put(3, 3);
    System.out.println(lru2);
    lru2.get(1);
    System.out.println(lru2);
    lru2.put(4, 4);
    lru2.put(5, 5);
    lru2.put(6, 6);
    System.out.println(lru2);
  }
}

運行結果:

FIFO

FIFO就是先進先出,可以使用LinkedHashMap進行實現(xiàn)。

當?shù)谌齻€參數(shù)傳入為false或者是默認的時候,就可以實現(xiàn)按插入順序排序,就可以實現(xiàn)FIFO緩存了。

/**
 * Constructs an empty <tt>LinkedHashMap</tt> instance with the
 * specified initial capacity, load factor and ordering mode.
 *
 * @param initialCapacity the initial capacity
 * @param loadFactor   the load factor
 * @param accessOrder   the ordering mode - <tt>true</tt> for
 *     access-order, <tt>false</tt> for insertion-order
 * @throws IllegalArgumentException if the initial capacity is negative
 *     or the load factor is nonpositive
 */
public LinkedHashMap(int initialCapacity,
           float loadFactor,
           boolean accessOrder) {
  super(initialCapacity, loadFactor);
  this.accessOrder = accessOrder;
}

實現(xiàn)代碼跟上述使用LinkedHashMap實現(xiàn)LRU的代碼基本一致,主要就是構造函數(shù)的傳值有些不同。

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class LRU1<K, V> {
  private final int MAX_CACHE_SIZE;
  private final float DEFAULT_LOAD_FACTORY = 0.75f;

  LinkedHashMap<K, V> map;

  public LRU1(int cacheSize) {
    MAX_CACHE_SIZE = cacheSize;
    int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1;
    /*
     * 第三個參數(shù)設置為true,代表linkedlist按訪問順序排序,可作為LRU緩存
     * 第三個參數(shù)設置為false,代表按插入順序排序,可作為FIFO緩存
     */
    map = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, false) {
      @Override
      protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > MAX_CACHE_SIZE;
      }
    };
  }

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

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

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

  public synchronized Set<Map.Entry<K, V>> getAll() {
    return map.entrySet();
  }

  @Override
  public String toString() {
    StringBuilder stringBuilder = new StringBuilder();
    for (Map.Entry<K, V> entry : map.entrySet()) {
      stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue()));
    }
    return stringBuilder.toString();
  }

  public static void main(String[] args) {
    LRU1<Integer, Integer> lru1 = new LRU1<>(5);
    lru1.put(1, 1);
    lru1.put(2, 2);
    lru1.put(3, 3);
    System.out.println(lru1);
    lru1.get(1);
    System.out.println(lru1);
    lru1.put(4, 4);
    lru1.put(5, 5);
    lru1.put(6, 6);
    System.out.println(lru1);
  }
}

運行結果:

以上就是使用Java實現(xiàn)這兩種緩存的方式,從中可以看出,LinkedHashMap實現(xiàn)緩存較為容易,因為底層函數(shù)對此已經(jīng)有了支持,自己編寫鏈表實現(xiàn)LRU緩存也是借鑒了LinkedHashMap中實現(xiàn)的思想。在Java中不只是這兩種數(shù)據(jù)結構可以實現(xiàn)緩存,比如ConcurrentHashMap、WeakHashMap在某些場景下也是可以作為緩存的,到底用哪一種數(shù)據(jù)結構主要是看場景再進行選擇,但是很多思想都是可以通用的。

相關文章

  • Java深入分析了解平衡二叉樹

    Java深入分析了解平衡二叉樹

    平衡二叉樹又被稱為AVL樹(有別于AVL算法),且具有以下性質(zhì):它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。本文將詳解介紹一下平衡二叉樹的原理與實現(xiàn),需要的可以參考一下
    2022-06-06
  • 詳解如何使用SpringBoot封裝Excel生成器

    詳解如何使用SpringBoot封裝Excel生成器

    在軟件開發(fā)過程中,經(jīng)常需要生成Excel文件來導出數(shù)據(jù)或者生成報表,為了簡化開發(fā)流程和提高代碼的可維護性,我們可以使用Spring Boot封裝Excel生成器,本文將介紹如何使用Spring Boot封裝Excel生成器,并提供一些示例代碼來說明其用法和功能
    2023-06-06
  • java中實現(xiàn)list或set轉(zhuǎn)map的方法

    java中實現(xiàn)list或set轉(zhuǎn)map的方法

    這篇文章主要介紹了java中實現(xiàn)list或set轉(zhuǎn)map的方法的相關資料,需要的朋友可以參考下
    2017-01-01
  • java中List集合子類特點淺析

    java中List集合子類特點淺析

    java.util.List接口繼承自Collection接口,是單列集合的一個重要分支,習慣性地會將實現(xiàn)了List接口的對象稱為List集合,下面這篇文章主要給大家介紹了關于java中List集合子類特點的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-01-01
  • Java實現(xiàn)手寫自旋鎖的示例代碼

    Java實現(xiàn)手寫自旋鎖的示例代碼

    自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它在內(nèi)核中大量應用于中斷處理等部分。本文將用Java實現(xiàn)手寫自旋鎖,需要的可以參考一下
    2022-08-08
  • Java語言實現(xiàn)基數(shù)排序代碼分享

    Java語言實現(xiàn)基數(shù)排序代碼分享

    這篇文章主要介紹了Java語言實現(xiàn)基數(shù)排序代碼分享,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • java上界通配符(? extends Type)的使用

    java上界通配符(? extends Type)的使用

    在Java中,? extends Type是一個上界通配符,本文主要介紹了java上界通配符(? extends Type)的使用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 在SpringBoot環(huán)境中使用Mockito進行單元測試的示例詳解

    在SpringBoot環(huán)境中使用Mockito進行單元測試的示例詳解

    Mockito特別適用于在Spring Boot環(huán)境中進行單元測試,因為它能夠輕松模擬Spring應用中的服務、存儲庫、客戶端和其他組件,通過使用Mockito,開發(fā)者可以模擬外部依賴,從而使單元測試更加獨立和可靠,本文給大家介紹了在Spring Boot環(huán)境中使用Mockito進行單元測試
    2024-01-01
  • Java冒泡排序法和選擇排序法的實現(xiàn)

    Java冒泡排序法和選擇排序法的實現(xiàn)

    這篇文章主要介紹了Java冒泡排序法和選擇排序法的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • java項目中的多線程實踐記錄

    java項目中的多線程實踐記錄

    項目開發(fā)中對于一些數(shù)據(jù)的處理需要用到多線程,比如文件的批量上傳,數(shù)據(jù)庫的分批寫入,大文件的分段下載等,主要涉及到多線程的一些知識,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下
    2021-11-11

最新評論