日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 架構師社區(qū)
[導讀]LRU全稱 "Least Recently Used",最近最少使用策略,判斷最近被使用的時間,距離目前最遠的數(shù)據(jù)優(yōu)先被淘汰,作為一種根據(jù)訪問時間來更改鏈表順序從而實現(xiàn)緩存淘汰的算法,它是redis采用的淘汰算法之一。

      
LRU全稱 "Least Recently Used",最近最少使用策略,判斷最近被使用的時間,距離目前最遠的數(shù)據(jù)優(yōu)先被淘汰,作為一種根據(jù)訪問時間來更改鏈表順序從而實現(xiàn)緩存淘汰的算法,它是redis采用的淘汰算法之一。redis還有一個緩存策略叫做LFU, 那么LFU是什么呢?

我們本期來分析一下LFU:

LFU是什么

LFU,全稱是:Least Frequently Used,最不經常使用策略,在一段時間內,數(shù)據(jù)被使用頻次最少的,優(yōu)先被淘汰。最少使用LFU)是一種用于管理計算機內存的緩存算法。主要是記錄和追蹤內存塊的使用次數(shù),當緩存已滿并且需要更多空間時,系統(tǒng)將以最低內存塊使用頻率清除內存.采用LFU算法的最簡單方法是為每個加載到緩存的塊分配一個計數(shù)器。每次引用該塊時,計數(shù)器將增加一。當緩存達到容量并有一個新的內存塊等待插入時,系統(tǒng)將搜索計數(shù)器最低的塊并將其從緩存中刪除(本段摘自維基百科)

字節(jié)二面,讓寫一個LFU緩存策略算法,懵了

上面這個圖就是一個LRU的簡單實現(xiàn)思路,在鏈表的開始插入元素,然后每插入一次計數(shù)一次,接著按照次數(shù)重新排序鏈表,如果次數(shù)相同的話,按照插入時間排序,然后從鏈表尾部選擇淘汰的數(shù)據(jù)~

LRU實現(xiàn)

2.1 定義Node節(jié)點

Node主要包含了key和value,因為LFU的主要實現(xiàn)思想是比較訪問的次數(shù),如果在次數(shù)相同的情況下需要比較節(jié)點的時間,越早放入的越快      被淘汰,因此我們需要在Node節(jié)點上加入time和count的屬性,分別用來記錄節(jié)點的訪問的時間和訪問次數(shù)。其他的版本實現(xiàn)方式里有新加個內部類來記錄 key的count和time,但是我覺得不夠方便,還得單獨維護一個map,成本有點大。還有一點注意的是這里實現(xiàn)了comparable接口,覆寫了compare方法,這里 的主要作用就是在排序的時候需要用到,在compare方法里面我們首先比較節(jié)點的訪問次數(shù),在訪問次數(shù)相同的情況下比較節(jié)點的訪問時間,這里是為了 在排序方法里面通過比較key來選擇淘汰的key

/** * 節(jié)點 */ public static class Node implements Comparable<Node>{ //鍵 Object key; //值 Object value; /** * 訪問時間 */ long time; /** * 訪問次數(shù) */ int count; public Node(Object key, Object value, long time, int count) { this.key = key; this.value = value; this.time = time; this.count = count;
    } public Object getKey() { return key;
    } public void setKey(Object key) { this.key = key;
    } public Object getValue() { return value;
    } public void setValue(Object value) { this.value = value;
    } public long getTime() { return time;
    } public void setTime(long time) { this.time = time;
    } public int getCount() { return count;
    } public void setCount(int count) { this.count = count;
    } @Override public int compareTo(Node o) { int compare = Integer.compare(this.count, o.count); //在數(shù)目相同的情況下 比較時間 if (compare==0){ return Long.compare(this.time,o.time);
        } return compare;
    }
}
字節(jié)二面,讓寫一個LFU緩存策略算法,懵了

2.2:定義LFU類

定義LFU類,這里采用了泛型,聲明了K和V,還有總容量和一個Map(caches)用來維護所有的節(jié)點。在構造方法里將size傳遞進去,并且創(chuàng)建了一個LinkedHashMap,采用linkedHashMap的主要原因是維護key的順序

public class LFU<KV> { /** *  總容量 */ private int capacity; /** * 所有的node節(jié)點 */ private Map caches; /** * 構造方法
     * @param size */ public LFU(int size) { this.capacity = size;
       caches = new LinkedHashMap(size);
    }
}

2.3: 添加元素

添加元素的邏輯主要是先從緩存中根據(jù)key獲取節(jié)點,如果獲取不到,證明是新添加的元素,然后和容量比較,大于預定容量的話,需要找出count計數(shù)最小(計數(shù)相同的情況下,選擇時間最久)的節(jié)點,然后移除掉那個。如果在預定的大小之內,就新創(chuàng)建節(jié)點,注意這里不能使用 System.currentTimeMillis()方法,因為毫秒級別的粒度無法對插入的時間進行區(qū)分,在運行比較快的情況下,只有System.nanoTime()才可以將key的插入時間區(qū)分,默認設置count計數(shù)為1.如果能獲取到,表示是舊的元素,那么就用新值覆蓋舊值,計數(shù)+1,設置key的time為當前納秒時間。最后還需要進行排序,這里可以看出插入元素的邏輯主要是添加進入緩存,更新元素的時間和計數(shù)~

/** * 添加元素
 * @param key
 * @param value */ public void put(K key, V value) {
    Node node = caches.get(key); //如果新元素 if (node == null) { //如果超過元素容納量 if (caches.size() >= capacity) { //移除count計數(shù)最小的那個key K leastKey = removeLeastCount();
            caches.remove(leastKey);
        } //創(chuàng)建新節(jié)點 node = new Node(key,value,System.nanoTime(),1);
        caches.put(key, node);
    }else { //已經存在的元素覆蓋舊值 node.value = value;
        node.setCount(node.getCount()+1);
        node.setTime(System.nanoTime());
    }
    sort();
}

每次put或者get元素都需要進行排序,排序的主要意義在于按照key的cout和time進行一個key順序的重組,這里的邏輯是首先將緩存map創(chuàng)建成一個list,然后按照Node的value進行,重組整個map。然后將原來的緩存清空,遍歷這個map, 把key和value的值放進去原來的緩存中的順序就進行了重組~這里區(qū)分于LRU的不同點在于使用了java的集合API,LRU的排序是進行節(jié)點移動。而在LFU中實現(xiàn)比較復雜,因為put的時候不光得比較基數(shù)還有時間。如果不借助java的API的話,可以新維護一個節(jié)點頻率鏈表,每次將key保存在這個節(jié)點頻率鏈表中移動指針,從而也間接可以實現(xiàn)排序~

/** * 排序 */ private void sort() {

    List> list = new ArrayList<>(caches.entrySet());
    Collections.sort(list, new Comparator>() { @Override public int compare(Map.Entry o1, Map.Entry o2) { return o2.getValue().compareTo(o1.getValue());
        }
    });

    caches.clear(); for (Map.Entry kNodeEntry : list) {
        caches.put(kNodeEntry.getKey(),kNodeEntry.getValue());
    }
}

** 移除最小的元素**

淘汰最小的元素這里調用了Collections.min方法,然后通過比較key的compare方法,找到計數(shù)最小和時間最長的元素,直接從緩存中移除~

/** * 移除統(tǒng)計數(shù)或者時間比較最小的那個
 * @return */ private K removeLeastCount() {
    Collectionvalues = caches.values();
    Node min = Collections.min(values); return (K)min.getKey();

 }

2.4:獲取元素

獲取元素首先是從緩存map中獲取,否則返回null,在獲取到元素之后需要進行節(jié)點的更新,計數(shù)+1和刷新節(jié)點的時間,根據(jù)LFU的原則,在當前時間獲取到這個節(jié)點以后,這個節(jié)點就暫時變成了熱點節(jié)點,但是它的cout計數(shù)也有可能是小于某個節(jié)點的count的,所以

此時不能將它直接移動到鏈表頂,還需要進行一次排序,重組它的位置~

/** * 獲取元素
 * @param key
 * @return */ public V get(K key){
Node node = caches.get(key); if (node!=null){
    node.setCount(node.getCount()+1);
    node.setTime(System.nanoTime());
    sort(); return (V)node.value;
} return null;
}

測試

首先聲明一個LRU,然后默認的最大的大小為5,依次put進入A、B、C、D、E、F6個元素,此時將會找到計數(shù)最小和時間最短的元素,那么將會淘汰A(因為count值都是1)。記著get兩次B元素,那么B元素的count=3,時間更新為最新。此時B將會移動到頂,接著在getC元素,C元素的count=2,時間會最新,那么此時因為它的count值依然小于B,所以它依然在B后面,再getF元素,F(xiàn)元素的count=2,又因為它的時間會最新,所以在與C相同的計數(shù)下,F(xiàn)元素更新(時間距離現(xiàn)在最近),所以鏈表將會移動,F(xiàn)會在C的前面,再次put一次C,此時C的count=3,同時時間為最新,那么此刻C的count和B保持一致,則他們比較時間,C明顯更新,所以C將會排在B的前面,最終的順序應該是:C->B->F->E->D

public static void main(String[] args) {

    LFU lruCache = new LFU<>(5);
    lruCache.put(1, "A");
    lruCache.put(2, "B");
    lruCache.put(3, "C");
    lruCache.put(4"D");
    lruCache.put(5, "E");
    lruCache.put(6, "F");
    lruCache.get(2);
    lruCache.get(2);
    lruCache.get(3);
    lruCache.get(6); //重新put節(jié)點3 lruCache.put(3,"C"); final Map caches = (Map) lruCache.caches; for (Map.Entry nodeEntry : caches.entrySet()) { 
        System.out.println(nodeEntry.getValue().value); 
    } 
}

運行結果:

字節(jié)二面,讓寫一個LFU緩存策略算法,懵了

LRU和LFU的區(qū)別以及LFU的缺點

LRU和LFU側重點不同,LRU主要體現(xiàn)在對元素的使用時間上,而LFU主要體現(xiàn)在對元素的使用頻次上。LFU的缺陷是:在短期的時間內,對某些緩存的訪問頻次很高,這些緩存會立刻晉升為熱點數(shù)據(jù),而保證不會淘汰,這樣會駐留在系統(tǒng)內存里面。而實際上,這部分數(shù)據(jù)只是短暫的高頻率訪問,之后將會長期不訪問,瞬時的高頻訪問將會造成這部分數(shù)據(jù)的引用頻率加快,而一些新加入的緩存很容易被快速刪除,因為它們的引用頻率很低。

總結

本篇博客針對LFU做了一個簡單的介紹,并詳細介紹了如何用java來實現(xiàn)LFU,并且和LRU做了一個簡單的比較。針對一種緩存策略。LFU有自己的獨特使用場景,如何實現(xiàn)LFU和利用LFU的特性來實現(xiàn)開發(fā)過程部分功能是我們需要思考的。實際在使用中LRU的使用頻率要高于LFU,不過了解這種算法也算是程序員的必備技能。



		
		
		

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據(jù)LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉