문제

시 말하지 않 EHCache 또는 OSCache,etc.가정을 위한 목적으로 이는 질문을 구현하려는 내 자신만을 이용하여 SDK(수행하여 학습).어는 캐쉬에 사용됩니다 다중 스레드 환경 데이터 구조체 것을 사용하나요?나는 이미 구현을 사용하여 하나 사용컬렉션#synchronizedMap, 지 궁금하기도 해요 어떤 경우의 새로운 동시 컬렉션은 더 나은 것 후보자.

업데이트:나는 그냥 읽기를 통해 Yegge 의 최신 을 발견했을 때 이 덩어리:

필요하신 경우는 일정한 시간 접근을 유지하려면 삽입하기 위해,당신은 할 수 없습니다 보다 더 나은 사용,진정으로 훌륭한 데이터 구조입니다.유일한 방법은 가능성이있을 수있는 더 멋진 경우가 있었는 동시 버전입니다.그러나 슬프도다.

나는 생각하고 있었다는 거의 정확히 같은 것은 앞으로 갔다 LinkedHashMap + Collections#synchronizedMap 구현 언급했다.알고 좋은 난 그냥 간과 무언가이다.

대답을 기반으로 지금까지,그것을 소리 내기위한 가장 좋은 매우 동시 LRU 을 연장하는 것 Concurrenthashmap 의 를 사용하여 어떤의 동일한 로직 LinkedHashMap 사용합니다.

도움이 되었습니까?

해결책 3

오늘 다시 처음 부터이 작업을 수행했다면 Guava 's를 사용할 것입니다. CacheBuilder.

다른 팁

나는이 제안들을 많이 좋아하지만 지금은 내가 고수 할 것 같아요 LinkedHashMap + Collections.synchronizedMap. 앞으로 이것을 다시 방문한다면 아마도 확장을 위해 노력할 것입니다. ConcurrentHashMap 같은 방법으로 LinkedHashMap 확장 HashMap.

업데이트:

요청에 따라 다음은 현재 내 구현의 요지입니다.

private class LruCache<A, B> extends LinkedHashMap<A, B> {
    private final int maxEntries;

    public LruCache(final int maxEntries) {
        super(maxEntries + 1, 1.0f, true);
        this.maxEntries = maxEntries;
    }

    /**
     * Returns <tt>true</tt> if this <code>LruCache</code> has more entries than the maximum specified when it was
     * created.
     *
     * <p>
     * This method <em>does not</em> modify the underlying <code>Map</code>; it relies on the implementation of
     * <code>LinkedHashMap</code> to do that, but that behavior is documented in the JavaDoc for
     * <code>LinkedHashMap</code>.
     * </p>
     *
     * @param eldest
     *            the <code>Entry</code> in question; this implementation doesn't care what it is, since the
     *            implementation is only dependent on the size of the cache
     * @return <tt>true</tt> if the oldest
     * @see java.util.LinkedHashMap#removeEldestEntry(Map.Entry)
     */
    @Override
    protected boolean removeEldestEntry(final Map.Entry<A, B> eldest) {
        return super.size() > maxEntries;
    }
}

Map<String, String> example = Collections.synchronizedMap(new LruCache<String, String>(CACHE_SIZE));

이것은 두 개의 라운드.

첫 번째 라운드가 무엇을 내놓았음을 다시 읽는 의견을 도메인과 조금 더 많은 뿌리 깊은 나의 머리에서.

그래서 여기서는 간단한 버전으로 단위의 테스트는 그것을 기반으로 작품에 몇 가지 다른 버전입니다.

첫 번째 비 동시 버전:

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

public class LruSimpleCache<K, V> implements LruCache <K, V>{

    Map<K, V> map = new LinkedHashMap (  );


    public LruSimpleCache (final int limit) {
           map = new LinkedHashMap <K, V> (16, 0.75f, true) {
               @Override
               protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
                   return super.size() > limit;
               }
           };
    }
    @Override
    public void put ( K key, V value ) {
        map.put ( key, value );
    }

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

    //For testing only
    @Override
    public V getSilent ( K key ) {
        V value =  map.get ( key );
        if (value!=null) {
            map.remove ( key );
            map.put(key, value);
        }
        return value;
    }

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

    @Override
    public int size () {
        return map.size ();
    }

    public String toString() {
        return map.toString ();
    }


}

참 깃발을 추적의 접근을 가져오고.보 JavaDocs.이 removeEdelstEntry 없이 진정한 플래그를 생성자 그냥 구현 FIFO 에 캐시(아래 참고 참조에 FIFO 및 removeEldestEntry).

여기에는 이 테스트는 그것을 증명한 작품으로 LRU 캐시:

public class LruSimpleTest {

    @Test
    public void test () {
        LruCache <Integer, Integer> cache = new LruSimpleCache<> ( 4 );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();


        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();


        if ( !ok ) die ();

    }

지금은 동시 버전...

패키지 org.혜택이 있습니다.캐시;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LruSimpleConcurrentCache<K, V> implements LruCache<K, V> {

    final CacheMap<K, V>[] cacheRegions;


    private static class CacheMap<K, V> extends LinkedHashMap<K, V> {
        private final ReadWriteLock readWriteLock;
        private final int limit;

        CacheMap ( final int limit, boolean fair ) {
            super ( 16, 0.75f, true );
            this.limit = limit;
            readWriteLock = new ReentrantReadWriteLock ( fair );

        }

        protected boolean removeEldestEntry ( final Map.Entry<K, V> eldest ) {
            return super.size () > limit;
        }


        @Override
        public V put ( K key, V value ) {
            readWriteLock.writeLock ().lock ();

            V old;
            try {

                old = super.put ( key, value );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return old;

        }


        @Override
        public V get ( Object key ) {
            readWriteLock.writeLock ().lock ();
            V value;

            try {

                value = super.get ( key );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;
        }

        @Override
        public V remove ( Object key ) {

            readWriteLock.writeLock ().lock ();
            V value;

            try {

                value = super.remove ( key );
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;

        }

        public V getSilent ( K key ) {
            readWriteLock.writeLock ().lock ();

            V value;

            try {

                value = this.get ( key );
                if ( value != null ) {
                    this.remove ( key );
                    this.put ( key, value );
                }
            } finally {
                readWriteLock.writeLock ().unlock ();
            }
            return value;

        }

        public int size () {
            readWriteLock.readLock ().lock ();
            int size = -1;
            try {
                size = super.size ();
            } finally {
                readWriteLock.readLock ().unlock ();
            }
            return size;
        }

        public String toString () {
            readWriteLock.readLock ().lock ();
            String str;
            try {
                str = super.toString ();
            } finally {
                readWriteLock.readLock ().unlock ();
            }
            return str;
        }


    }

    public LruSimpleConcurrentCache ( final int limit, boolean fair ) {
        int cores = Runtime.getRuntime ().availableProcessors ();
        int stripeSize = cores < 2 ? 4 : cores * 2;
        cacheRegions = new CacheMap[ stripeSize ];
        for ( int index = 0; index < cacheRegions.length; index++ ) {
            cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
        }
    }

    public LruSimpleConcurrentCache ( final int concurrency, final int limit, boolean fair ) {

        cacheRegions = new CacheMap[ concurrency ];
        for ( int index = 0; index < cacheRegions.length; index++ ) {
            cacheRegions[ index ] = new CacheMap<> ( limit / cacheRegions.length, fair );
        }
    }

    private int stripeIndex ( K key ) {
        int hashCode = key.hashCode () * 31;
        return hashCode % ( cacheRegions.length );
    }

    private CacheMap<K, V> map ( K key ) {
        return cacheRegions[ stripeIndex ( key ) ];
    }

    @Override
    public void put ( K key, V value ) {

        map ( key ).put ( key, value );
    }

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

    //For testing only
    @Override
    public V getSilent ( K key ) {
        return map ( key ).getSilent ( key );

    }

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

    @Override
    public int size () {
        int size = 0;
        for ( CacheMap<K, V> cache : cacheRegions ) {
            size += cache.size ();
        }
        return size;
    }

    public String toString () {

        StringBuilder builder = new StringBuilder ();
        for ( CacheMap<K, V> cache : cacheRegions ) {
            builder.append ( cache.toString () ).append ( '\n' );
        }

        return builder.toString ();
    }


}

왜 당신이 볼 수있는 커버 비 동시 버전이 처음이다.위을 만들려고 일부는 스트라이프를 줄이는 자물쇠 경쟁이 일어납니다.그래서 우리는 이 해시 키고 그런 다음 보이는 해시을 찾기 위해 실제 캐시입니다.이것은 제한 크기 이상의 제안/거친 추측에서 공정한 금액의 오류에 따라서는 방법론 확산을 키를 해시 알고리즘입니다.

여기에서 테스트하는 동시 버전을 아마도 작동합니다.:)(에 따라 테스트를 화재 될 것이 실제 방법).

public class SimpleConcurrentLRUCache {


    @Test
    public void test () {
        LruCache <Integer, Integer> cache = new LruSimpleConcurrentCache<> ( 1, 4, false );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );

        puts (cache);
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();


        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();

        cache.put ( 8, 8 );
        cache.put ( 9, 9 );

        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();


        puts (cache);


        if ( !ok ) die ();

    }


    @Test
    public void test2 () {
        LruCache <Integer, Integer> cache = new LruSimpleConcurrentCache<> ( 400, false );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        for (int index =0 ; index < 5_000; index++) {
            cache.get(0);
            cache.get ( 1 );
            cache.put ( 2, index  );
            cache.put ( 3, index );
            cache.put(index, index);
        }

        boolean ok = cache.getSilent ( 0 ) == 0 || die ();
        ok |= cache.getSilent ( 1 ) == 1 || die ();
        ok |= cache.getSilent ( 2 ) != null || die ();
        ok |= cache.getSilent ( 3 ) != null || die ();

        ok |= cache.size () < 600 || die();
        if ( !ok ) die ();



    }

}

이것은 마지막 게시물을..첫번째 게시물을 삭제했으로 그것이었 lfu 에하지 않는 LRU 캐시입니다.

내가 내 생각이 다른 이동합니다.내가 하려고 노력하고 함께 올 가장 간단한 버전의 LRU 캐시를 사용하는 표준 JDK w/o 너무 많이 구현됩니다.

여기에는 무엇을 합니다.내 첫 번째 시도 약간의 재해로 구현 lfu 에 대신과 LRU,그리고 나는 추가 FIFO,and LRU 지원하...그리고 이것이 되고 있었다 괴물이다.그때 나는 이야기를 시작하는 나의 친구이 요한이었던 거의 관심이,그리고 나서에서 설명한 깊은 길이 어떻게 구현되는 lfu 에 LRU 는 FIFO 어떻게 전환할 수 있으로 간단하 ENUM arg,다음을 깨달았다는 모든 내가 정말 원하는 간단하 LRU.그래서를 무시 이전 게시물이 내게서부터 나를 알고를 보시려면 LRU/lfu 에/FIFO 에 캐시 바꿀 수 있을 통해 enum...아니오?Ok..여기에는 이동합니다.

가장 간단한 가능한 LRU 만 사용하는 JDK.나는 모두 구현은 동시 버전과 비 동시 버전입니다.

내가 만들어 공통 인터페이스(그것은 미니멀리즘이 그래서 가능성이 없는 몇 가지 기능을 하는 것 같지만 그것은 작품에 대한 내용의 경우,그러나 만약 당신이보고 싶은 기능을 XYZ 알려주세요...나는 라이브 코드를 작성하.).

public interface LruCache<KEY, VALUE> {
    void put ( KEY key, VALUE value );

    VALUE get ( KEY key );

    VALUE getSilent ( KEY key );

    void remove ( KEY key );

    int size ();
}

가 무엇인지 궁금할 getSilent 입니다.내가 사용하는 이에 대한 테스트합니다.getSilent 변경되지 않 LRU 점수 항목입니다.

첫 번째 비 동시에 하나입니다....

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

public class LruCacheNormal<KEY, VALUE> implements LruCache<KEY,VALUE> {

    Map<KEY, VALUE> map = new HashMap<> ();
    Deque<KEY> queue = new LinkedList<> ();
    final int limit;


    public LruCacheNormal ( int limit ) {
        this.limit = limit;
    }

    public void put ( KEY key, VALUE value ) {
        VALUE oldValue = map.put ( key, value );

        /*If there was already an object under this key,
         then remove it before adding to queue
         Frequently used keys will be at the top so the search could be fast.
         */
        if ( oldValue != null ) {
            queue.removeFirstOccurrence ( key );
        }
        queue.addFirst ( key );

        if ( map.size () > limit ) {
            final KEY removedKey = queue.removeLast ();
            map.remove ( removedKey );
        }

    }


    public VALUE get ( KEY key ) {

        /* Frequently used keys will be at the top so the search could be fast.*/
        queue.removeFirstOccurrence ( key );
        queue.addFirst ( key );
        return map.get ( key );
    }


    public VALUE getSilent ( KEY key ) {

        return map.get ( key );
    }

    public void remove ( KEY key ) {

        /* Frequently used keys will be at the top so the search could be fast.*/
        queue.removeFirstOccurrence ( key );
        map.remove ( key );
    }

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

    public String toString() {
        return map.toString ();
    }
}

큐가 있습니다.removeFirstOccurrence 잠재적으로 비용이 많이 드는 작업이 큰 경우 캐시입니다.할 수 있 LinkedList 로 예 추가 역을 조회 해시 지도에서 요소를 노드를 제거 작업을 훨씬 더 빠르고 더 일관성입니다.시작도 하지만 깨달은 필요하지 않습니다.하지만...어쩌면...

을 넣 라,키에 추가됩니다.면 을 얻 라고 핵심을 얻을 제거하고 다시 추가되는 정상의합니다.

하는 경우 캐시고 작은 건물은 항목이 비싼 이야 좋은 캐시입니다.하는 경우 캐시은 정말 큰,다음 선형 검색이 될 수 있는 병 목 특히지 않는 경우에는 뜨거운 지역의 캐시입니다.더 강렬한 핫스팟,빠른 선형 검색으로 뜨거운 항목에서 항상 최고의 선형 검색합니다.어쨌든...을 위해 필요한 것은 이것을 빠르게 이동이 쓰는 또 다른 LinkedList 는 제거 작업이 있는 반전 요소를 노드 검색에 대한 제거,다음을 제거하는 것에 대한 빠른으로 제거에서 키를 해시 지도입니다.

이 있는 경우 캐시 아래에 1,000 개 이 운동해야 한다.

여기에는 간단한 시험을 표시하는 그것의 작업에서 작업입니다.

public class LruCacheTest {

    @Test
    public void test () {
        LruCache<Integer, Integer> cache = new LruCacheNormal<> ( 4 );


        cache.put ( 0, 0 );
        cache.put ( 1, 1 );

        cache.put ( 2, 2 );
        cache.put ( 3, 3 );


        boolean ok = cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 0 ) == 0 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();


        cache.put ( 4, 4 );
        cache.put ( 5, 5 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 0 ) == null || die ();
        ok |= cache.getSilent ( 1 ) == null || die ();
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == 4 || die ();
        ok |= cache.getSilent ( 5 ) == 5 || die ();

        if ( !ok ) die ();

    }
}

마지막 LRU 캐시던 하나의 스레드,과하지 마시기 바랍에 포장 동기화됩니다....

여기에는 곳에서 동시 버전입니다.

import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrentLruCache<KEY, VALUE> implements LruCache<KEY,VALUE> {

    private final ReentrantLock lock = new ReentrantLock ();


    private final Map<KEY, VALUE> map = new ConcurrentHashMap<> ();
    private final Deque<KEY> queue = new LinkedList<> ();
    private final int limit;


    public ConcurrentLruCache ( int limit ) {
        this.limit = limit;
    }

    @Override
    public void put ( KEY key, VALUE value ) {
        VALUE oldValue = map.put ( key, value );
        if ( oldValue != null ) {
            removeThenAddKey ( key );
        } else {
            addKey ( key );
        }
        if (map.size () > limit) {
            map.remove ( removeLast() );
        }
    }


    @Override
    public VALUE get ( KEY key ) {
        removeThenAddKey ( key );
        return map.get ( key );
    }


    private void addKey(KEY key) {
        lock.lock ();
        try {
            queue.addFirst ( key );
        } finally {
            lock.unlock ();
        }


    }

    private KEY removeLast( ) {
        lock.lock ();
        try {
            final KEY removedKey = queue.removeLast ();
            return removedKey;
        } finally {
            lock.unlock ();
        }
    }

    private void removeThenAddKey(KEY key) {
        lock.lock ();
        try {
            queue.removeFirstOccurrence ( key );
            queue.addFirst ( key );
        } finally {
            lock.unlock ();
        }

    }

    private void removeFirstOccurrence(KEY key) {
        lock.lock ();
        try {
            queue.removeFirstOccurrence ( key );
        } finally {
            lock.unlock ();
        }

    }


    @Override
    public VALUE getSilent ( KEY key ) {
        return map.get ( key );
    }

    @Override
    public void remove ( KEY key ) {
        removeFirstOccurrence ( key );
        map.remove ( key );
    }

    @Override
    public int size () {
        return map.size ();
    }

    public String toString () {
        return map.toString ();
    }
}

주요 차이의 사용 concurrenthashmap 의 대신 HashMap,그리고 사용의 자물쇠(가장 시급한 문제와 함께 멀리 동기화하지만,...).

을 테스트하지 않았습 그것은에서 불과 같은 간단한 LRU 캐시할 수 있는 작업의 80%에서 사용해야 하는 경우에는 간단한 LRU 지도입니다.

신 의견을 제외한 사용하지 않는 이유가 무엇 라이브러리 a,b,c.하지 않는 이유 항상 라이브러리를 사용하지 않기 때문에 항상 모든 전쟁에 파일을 수 80 메가바이트,내가 쓰고 라이브러리는 그래서 나는 라이브러리 플러그인 할 수 있으로 충분히 좋은 해결책이 장소에서 누군가할 수 있는 플러그인에서 또 다른 공급자 캐시 그들이 좋아하는 경우.:) 나는 결코 알지 못할 때 필요한 구아바나 ehcache 또는 다른 뭔가가 나는 포함하지 않지만 만약 내가 캐싱 플러그인할 수 있는,나는 제외되지 않습니다 그들 중 하나.

감소의 종속성을 그것의 자신의 보상입니다.나는 사랑을 얻는 방법에 대한 피드백을 만들이 간편하고 신속하게 수행할 수니다.

또한 누구나 알고있는 경우의 갈 준비가되어 있습니다....

Ok..나는 무엇을 생각하는...가지 않고 그냥 사용 removeEldest 입장에서 사용,그리고 잘 나가야 하지만..........하지만..하지만..는 것 FIFO 를 사용하지 않는 LRU 고 우리를 구현하기 위해 노력하고 LRU.

    Map<KEY, VALUE> map = new LinkedHashMap<KEY, VALUE> () {

        @Override
        protected boolean removeEldestEntry ( Map.Entry<KEY, VALUE> eldest ) {
            return this.size () > limit;
        }
    };

이 테스트 실패에 대한 위의 코드...

        cache.get ( 2 );
        cache.get ( 3 );
        cache.put ( 6, 6 );
        cache.put ( 7, 7 );
        ok |= cache.size () == 4 || die ( "size" + cache.size () );
        ok |= cache.getSilent ( 2 ) == 2 || die ();
        ok |= cache.getSilent ( 3 ) == 3 || die ();
        ok |= cache.getSilent ( 4 ) == null || die ();
        ok |= cache.getSilent ( 5 ) == null || die ();

그래서 여기에 빠르고 더러운 FIFO 사용하여 캐시 removeEldestEntry.

import java.util.*;

public class FifoCache<KEY, VALUE> implements LruCache<KEY,VALUE> {

    final int limit;

    Map<KEY, VALUE> map = new LinkedHashMap<KEY, VALUE> () {

        @Override
        protected boolean removeEldestEntry ( Map.Entry<KEY, VALUE> eldest ) {
            return this.size () > limit;
        }
    };


    public LruCacheNormal ( int limit ) {
        this.limit = limit;
    }

    public void put ( KEY key, VALUE value ) {
         map.put ( key, value );


    }


    public VALUE get ( KEY key ) {

        return map.get ( key );
    }


    public VALUE getSilent ( KEY key ) {

        return map.get ( key );
    }

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

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

    public String toString() {
        return map.toString ();
    }
}

Fifo 은 빠릅니다.검색니다.할 수 있습 앞 FIFO 앞에서 LRU 및 처리하는 것이 가장 뜨거운 항목을 정도로 좋습니다.더 나은 LRU 가 필요로 하는 반전 요소를 노드 기능입니다.

어쨌든...이제는 내가 쓴 일부 코드,나를 놓을 통해 다른 답변과 내가 무엇을 놓쳤...첫 번째 시간에 나는 그들을 스캔.

LinkedHashMap O (1)이지만 동기화가 필요합니다. 그곳에서 바퀴를 재발 명 할 필요가 없습니다.

동시성 증가를위한 2 가지 옵션 :

1. 여러 가지를 만듭니다 LinkedHashMap, 그들에게 해시 : 예 : LinkedHashMap[4], index 0, 1, 2, 3. 키에서 key%4 (또는 binary OR ~에 [key, 3]) put/get/remod를 수행 할 맵을 선택합니다.

2. 당신은 확장하여 '거의'lru를 할 수 있습니다. ConcurrentHashMap, 그리고 그 내부의 각 영역에서 구조와 같은 링크 된 해시 맵을 갖습니다. 잠금 장치는 a보다 더 세분화됩니다 LinkedHashMap 그것은 동기화됩니다. a put 또는 putIfAbsent 목록의 머리와 꼬리에 잠금 만 필요합니다 (지역 당). 제거하거나 전체 지역을 잠그십시오. Atomic 링크 된 목록이 여기서 도움이 될 수 있는지 궁금합니다. 아마도 목록의 헤드에 도움이 될 것입니다. 아마도 더.

구조는 총 주문을 유지하지 않고 지역 당 주문 만 유지합니다. 항목의 수가 영역 수보다 훨씬 크면 대부분의 캐시에 충분합니다. 각 지역에는 자체 입력 카운트가 있어야합니다. 이는 퇴거 방아쇠의 글로벌 수보다는 사용됩니다. a ConcurrentHashMap 16 세는 오늘날 대부분의 서버에 충분합니다.

  1. 적당한 동시성 하에서 쓰기가 더 쉬우 며 더 빠릅니다.

  2. 글을 쓰는 것이 더 어렵지만 매우 높은 동시성에서는 훨씬 더 잘 확장됩니다. 정상적인 액세스가 느려질 것입니다 (마찬가지로 ConcurrentHashMap 보다 느립니다 HashMap 동시성이없는 곳)

두 가지 오픈 소스 구현이 있습니다.

Apache Solr은 동의어를 가지고 있습니다. https://lucene.apache.org/solr/3_6_1/org/apache/solr/util/concurrentlrucache.html

동시 링크드 하슈 맵을위한 오픈 소스 프로젝트가 있습니다.http://code.google.com/p/concurrentlinkedhashmap/

나는 사용을 고려할 것이다 java.util.concurrent.priorityBlockingqueue, 각 요소에서 "번호 부서"카운터에 의해 결정된 우선 순위. 나는 할 것이다 매우 조심합니다 "숫자 오퍼스"카운터는 요소가 불변 할 수 없음을 의미하므로 모든 동기화를 올바르게 얻으려면.

요소 객체는 캐시의 객체의 래퍼입니다.

class CacheElement {
    private final Object obj;
    private int numberOfUsers = 0;

    CacheElement(Object obj) {
        this.obj = obj;
    }

    ... etc.
}

도움이 되었기를 바랍니다 .

import java.util.*;
public class Lru {

public static <K,V> Map<K,V> lruCache(final int maxSize) {
    return new LinkedHashMap<K, V>(maxSize*4/3, 0.75f, true) {

        private static final long serialVersionUID = -3588047435434569014L;

        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxSize;
        }
    };
 }
 public static void main(String[] args ) {
    Map<Object, Object> lru = Lru.lruCache(2);      
    lru.put("1", "1");
    lru.put("2", "2");
    lru.put("3", "3");
    System.out.println(lru);
}
}

LRU 캐시는 동시에 린드 큐어와 멀티 스레딩 시나리오에서도 사용할 수있는 동의식 맵을 사용하여 구현할 수 있습니다. 대기열의 헤드는 대기열에 가장 오랫동안 사용 된 요소입니다. 대기열의 꼬리는 대기열에 가장 짧은 시간에있는 요소입니다. 맵에 요소가 존재하면 링크드 큐에서 제거하여 꼬리에 삽입 할 수 있습니다.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public class LRUCache<K,V> {
  private ConcurrentHashMap<K,V> map;
  private ConcurrentLinkedQueue<K> queue;
  private final int size; 

  public LRUCache(int size) {
    this.size = size;
    map = new ConcurrentHashMap<K,V>(size);
    queue = new ConcurrentLinkedQueue<K>();
  }

  public V get(K key) {
    //Recently accessed, hence move it to the tail
    queue.remove(key);
    queue.add(key);
    return map.get(key);
  }

  public void put(K key, V value) {
    //ConcurrentHashMap doesn't allow null key or values
    if(key == null || value == null) throw new NullPointerException();
    if(map.containsKey(key) {
      queue.remove(key);
    }
    if(queue.size() >= size) {
      K lruKey = queue.poll();
      if(lruKey != null) {
        map.remove(lruKey);
      }
    }
    queue.add(key);
    map.put(key,value);
  }

}

LRU에 대한 제 구현은 다음과 같습니다. 기본적으로 FIFO로 작동하는 우선권 큐를 사용했습니다. Page Time Creation을 기반으로 사용하고 최근에 가장 최근에 사용되는 시간 동안 페이지의 순서를 수행합니다.

고려할 페이지 : 2, 1, 0, 2, 8, 2, 4

캐시에 추가 된 페이지는 다음과 같습니다
캐시에 추가 된 페이지는 다음과 같습니다. 1
캐시에 추가 된 페이지는 다음과 같습니다
페이지 : 2 이미 캐시에 진학합니다. 마지막으로 액세스 한 시간 업데이트
페이지 결함, 페이지 : 1, 페이지로 교체 : 8
캐시에 추가 된 페이지는 다음과 같습니다
페이지 : 2 이미 캐시에 진학합니다. 마지막으로 액세스 한 시간 업데이트
페이지 결함, 페이지 : 0, 페이지로 교체 : 4
캐시에 추가 된 페이지는 다음과 같습니다

산출

lrucache 페이지
-------------
PageName : 8, PageCreationTime : 1365957019974
PageName : 2, PageCreationTime : 1365957020074
PageName : 4, PageCreationTime : 1365957020174

여기에 코드를 입력하시오

import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;


public class LRUForCache {
    private PriorityQueue<LRUPage> priorityQueue = new PriorityQueue<LRUPage>(3, new LRUPageComparator());
    public static void main(String[] args) throws InterruptedException {

        System.out.println(" Pages for consideration : 2, 1, 0, 2, 8, 2, 4");
        System.out.println("----------------------------------------------\n");

        LRUForCache cache = new LRUForCache();
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("1"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("0"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("8"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("2"));
        Thread.sleep(100);
        cache.addPageToQueue(new LRUPage("4"));
        Thread.sleep(100);

        System.out.println("\nLRUCache Pages");
        System.out.println("-------------");
        cache.displayPriorityQueue();
    }


    public synchronized void  addPageToQueue(LRUPage page){
        boolean pageExists = false;
        if(priorityQueue.size() == 3){
            Iterator<LRUPage> iterator = priorityQueue.iterator();

            while(iterator.hasNext()){
                LRUPage next = iterator.next();
                if(next.getPageName().equals(page.getPageName())){
                    /* wanted to just change the time, so that no need to poll and add again.
                       but elements ordering does not happen, it happens only at the time of adding
                       to the queue

                       In case somebody finds it, plz let me know.
                     */
                    //next.setPageCreationTime(page.getPageCreationTime()); 

                    priorityQueue.remove(next);
                    System.out.println("Page: " + page.getPageName() + " already exisit in cache. Last accessed time updated");
                    pageExists = true;
                    break;
                }
            }
            if(!pageExists){
                // enable it for printing the queue elemnts
                //System.out.println(priorityQueue);
                LRUPage poll = priorityQueue.poll();
                System.out.println("Page Fault, PAGE: " + poll.getPageName()+", Replaced with PAGE: "+page.getPageName());

            }
        }
        if(!pageExists){
            System.out.println("Page added into cache is : " + page.getPageName());
        }
        priorityQueue.add(page);

    }

    public void displayPriorityQueue(){
        Iterator<LRUPage> iterator = priorityQueue.iterator();
        while(iterator.hasNext()){
            LRUPage next = iterator.next();
            System.out.println(next);
        }
    }
}

class LRUPage{
    private String pageName;
    private long pageCreationTime;
    public LRUPage(String pagename){
        this.pageName = pagename;
        this.pageCreationTime = System.currentTimeMillis();
    }

    public String getPageName() {
        return pageName;
    }

    public long getPageCreationTime() {
        return pageCreationTime;
    }

    public void setPageCreationTime(long pageCreationTime) {
        this.pageCreationTime = pageCreationTime;
    }

    @Override
    public boolean equals(Object obj) {
        LRUPage page = (LRUPage)obj; 
        if(pageCreationTime == page.pageCreationTime){
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return (int) (31 * pageCreationTime);
    }

    @Override
    public String toString() {
        return "PageName: " + pageName +", PageCreationTime: "+pageCreationTime;
    }
}


class LRUPageComparator implements Comparator<LRUPage>{

    @Override
    public int compare(LRUPage o1, LRUPage o2) {
        if(o1.getPageCreationTime() > o2.getPageCreationTime()){
            return 1;
        }
        if(o1.getPageCreationTime() < o2.getPageCreationTime()){
            return -1;
        }
        return 0;
    }
}

다음은 동기화 된 블록없이 테스트 된 최상의 성능 동시 LRU 캐시 구현입니다.

public class ConcurrentLRUCache<Key, Value> {

private final int maxSize;

private ConcurrentHashMap<Key, Value> map;
private ConcurrentLinkedQueue<Key> queue;

public ConcurrentLRUCache(final int maxSize) {
    this.maxSize = maxSize;
    map = new ConcurrentHashMap<Key, Value>(maxSize);
    queue = new ConcurrentLinkedQueue<Key>();
}

/**
 * @param key - may not be null!
 * @param value - may not be null!
 */
public void put(final Key key, final Value value) {
    if (map.containsKey(key)) {
        queue.remove(key); // remove the key from the FIFO queue
    }

    while (queue.size() >= maxSize) {
        Key oldestKey = queue.poll();
        if (null != oldestKey) {
            map.remove(oldestKey);
        }
    }
    queue.add(key);
    map.put(key, value);
}

/**
 * @param key - may not be null!
 * @return the value associated to the given key or null
 */
public Value get(final Key key) {
    return map.get(key);
}

}

이것은 내가 사용하는 LRU 캐시이며, 이는 링크드 하슈 맵을 캡슐화하고 동시성을 입은 단순한 동기화 잠금 장치로 수분이 많은 반점을 보호합니다. 그것은 사용되는 요소가 "가장 신선한"요소가되므로 실제로 LRU가되도록 사용되는 요소를 "터치"합니다. 또한 최소 수명이있는 내 요소의 요구 사항이 있었는데, "최대 유휴 시간"이 허용 된 것으로 생각할 수도 있습니다.

그러나 나는 행크의 결론에 동의하고 대답을 받아 들였다 - 오늘 다시 시작했다면, 나는 구아바의 것을 확인했다. CacheBuilder.

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


public class MaxIdleLRUCache<KK, VV> {

    final static private int IDEAL_MAX_CACHE_ENTRIES = 128;

    public interface DeadElementCallback<KK, VV> {
        public void notify(KK key, VV element);
    }

    private Object lock = new Object();
    private long minAge;
    private HashMap<KK, Item<VV>> cache;


    public MaxIdleLRUCache(long minAgeMilliseconds) {
        this(minAgeMilliseconds, IDEAL_MAX_CACHE_ENTRIES);
    }

    public MaxIdleLRUCache(long minAgeMilliseconds, int idealMaxCacheEntries) {
        this(minAgeMilliseconds, idealMaxCacheEntries, null);
    }

    public MaxIdleLRUCache(long minAgeMilliseconds, int idealMaxCacheEntries, final DeadElementCallback<KK, VV> callback) {
        this.minAge = minAgeMilliseconds;
        this.cache = new LinkedHashMap<KK, Item<VV>>(IDEAL_MAX_CACHE_ENTRIES + 1, .75F, true) {
            private static final long serialVersionUID = 1L;

            // This method is called just after a new entry has been added
            public boolean removeEldestEntry(Map.Entry<KK, Item<VV>> eldest) {
                // let's see if the oldest entry is old enough to be deleted. We don't actually care about the cache size.
                long age = System.currentTimeMillis() - eldest.getValue().birth;
                if (age > MaxIdleLRUCache.this.minAge) {
                    if ( callback != null ) {
                        callback.notify(eldest.getKey(), eldest.getValue().payload);
                    }
                    return true; // remove it
                }
                return false; // don't remove this element
            }
        };

    }

    public void put(KK key, VV value) {
        synchronized ( lock ) {
//          System.out.println("put->"+key+","+value);
            cache.put(key, new Item<VV>(value));
        }
    }

    public VV get(KK key) {
        synchronized ( lock ) {
//          System.out.println("get->"+key);
            Item<VV> item = getItem(key);
            return item == null ? null : item.payload;
        }
    }

    public VV remove(String key) {
        synchronized ( lock ) {
//          System.out.println("remove->"+key);
            Item<VV> item =  cache.remove(key);
            if ( item != null ) {
                return item.payload;
            } else {
                return null;
            }
        }
    }

    public int size() {
        synchronized ( lock ) {
            return cache.size();
        }
    }

    private Item<VV> getItem(KK key) {
        Item<VV> item = cache.get(key);
        if (item == null) {
            return null;
        }
        item.touch(); // idle the item to reset the timeout threshold
        return item;
    }

    private static class Item<T> {
        long birth;
        T payload;

        Item(T payload) {
            this.birth = System.currentTimeMillis();
            this.payload = payload;
        }

        public void touch() {
            this.birth = System.currentTimeMillis();
        }
    }

}

캐시의 경우 일반적으로 프록시 객체 (URL, 문자열 ....)를 통해 일부 데이터 조각을 찾게됩니다. 인터페이스 측면에서 맵을 원할 것입니다. 그러나 물건을 쫓아 내기 위해 구조와 같은 줄을 원합니다. 내부적으로 나는 우선 순위와 해시 맵의 두 가지 데이터 구조를 유지합니다. 그녀는 O (1) 시간에 모든 것을 수행 할 수 있어야하는 구현입니다.

여기에 내가 꽤 빨리 채찍질 된 수업이 있습니다.

import java.util.HashMap;
import java.util.Map;
public class LRUCache<K, V>
{
    int maxSize;
    int currentSize = 0;

    Map<K, ValueHolder<K, V>> map;
    LinkedList<K> queue;

    public LRUCache(int maxSize)
    {
        this.maxSize = maxSize;
        map = new HashMap<K, ValueHolder<K, V>>();
        queue = new LinkedList<K>();
    }

    private void freeSpace()
    {
        K k = queue.remove();
        map.remove(k);
        currentSize--;
    }

    public void put(K key, V val)
    {
        while(currentSize >= maxSize)
        {
            freeSpace();
        }
        if(map.containsKey(key))
        {//just heat up that item
            get(key);
            return;
        }
        ListNode<K> ln = queue.add(key);
        ValueHolder<K, V> rv = new ValueHolder<K, V>(val, ln);
        map.put(key, rv);       
        currentSize++;
    }

    public V get(K key)
    {
        ValueHolder<K, V> rv = map.get(key);
        if(rv == null) return null;
        queue.remove(rv.queueLocation);
        rv.queueLocation = queue.add(key);//this ensures that each item has only one copy of the key in the queue
        return rv.value;
    }
}

class ListNode<K>
{
    ListNode<K> prev;
    ListNode<K> next;
    K value;
    public ListNode(K v)
    {
        value = v;
        prev = null;
        next = null;
    }
}

class ValueHolder<K,V>
{
    V value;
    ListNode<K> queueLocation;
    public ValueHolder(V value, ListNode<K> ql)
    {
        this.value = value;
        this.queueLocation = ql;
    }
}

class LinkedList<K>
{
    ListNode<K> head = null;
    ListNode<K> tail = null;

    public ListNode<K> add(K v)
    {
        if(head == null)
        {
            assert(tail == null);
            head = tail = new ListNode<K>(v);
        }
        else
        {
            tail.next = new ListNode<K>(v);
            tail.next.prev = tail;
            tail = tail.next;
            if(tail.prev == null)
            {
                tail.prev = head;
                head.next = tail;
            }
        }
        return tail;
    }

    public K remove()
    {
        if(head == null)
            return null;
        K val = head.value;
        if(head.next == null)
        {
            head = null;
            tail = null;
        }
        else
        {
            head = head.next;
            head.prev = null;
        }
        return val;
    }

    public void remove(ListNode<K> ln)
    {
        ListNode<K> prev = ln.prev;
        ListNode<K> next = ln.next;
        if(prev == null)
        {
            head = next;
        }
        else
        {
            prev.next = next;
        }
        if(next == null)
        {
            tail = prev;
        }
        else
        {
            next.prev = prev;
        }       
    }
}

작동 방식은 다음과 같습니다. 키는 목록 앞쪽에 가장 오래된 키가있는 링크 된 목록에 저장됩니다 (새 키는 뒷면으로갑니다). 큐 앞쪽에서 팝업 한 다음 키를 사용하여 지도에서 값을 제거하십시오. 항목이 참조되면지도에서 부가 가치를 잡은 다음 queuelocation 변수를 사용하여 대기열의 현재 위치에서 키를 제거한 다음 큐의 뒷면 (현재 가장 최근에 사용)에 넣습니다. 물건을 추가하는 것은 거의 동일합니다.

여기에 많은 오류가 있다고 확신하며 동기화를 구현하지 않았습니다. 그러나이 클래스는 O (1) 캐시에 추가, O (1) 오래된 항목의 제거 및 O (1) 캐시 항목 검색을 제공합니다. 사소한 동기화 (모든 공개 방법으로 동기화)조차도 런타임으로 인해 여전히 잠금 경합이 거의 없습니다. 누군가가 영리한 동기화 트릭을 가지고 있다면 나는 매우 관심이있을 것입니다. 또한지도와 관련하여 MaxSize 변수를 사용하여 구현할 수있는 추가 최적화가 있다고 확신합니다.

살펴보십시오 ConcurrentsKipListMap. 캐시에 이미 포함되어있는 경우 요소를 테스트하고 제거 할 시간과 다시 개약 할 시간을 기록해야합니다.

LRU 순서의 순서를 강제하기 위해 카운터 등과 래퍼 요소가 필요하고 캐시가 가득 차면 최근에 물건이 폐기되도록하십시오.

여기 내 짧은 구현이 있습니다. 비판하거나 개선하십시오!

package util.collection;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Limited size concurrent cache map implementation.<br/>
 * LRU: Least Recently Used.<br/>
 * If you add a new key-value pair to this cache after the maximum size has been exceeded,
 * the oldest key-value pair will be removed before adding.
 */

public class ConcurrentLRUCache<Key, Value> {

private final int maxSize;
private int currentSize = 0;

private ConcurrentHashMap<Key, Value> map;
private ConcurrentLinkedQueue<Key> queue;

public ConcurrentLRUCache(final int maxSize) {
    this.maxSize = maxSize;
    map = new ConcurrentHashMap<Key, Value>(maxSize);
    queue = new ConcurrentLinkedQueue<Key>();
}

private synchronized void freeSpace() {
    Key key = queue.poll();
    if (null != key) {
        map.remove(key);
        currentSize = map.size();
    }
}

public void put(Key key, Value val) {
    if (map.containsKey(key)) {// just heat up that item
        put(key, val);
        return;
    }
    while (currentSize >= maxSize) {
        freeSpace();
    }
    synchronized(this) {
        queue.add(key);
        map.put(key, val);
        currentSize++;
    }
}

public Value get(Key key) {
    return map.get(key);
}
}

이 문제에 대한 내 자신의 구현은 다음과 같습니다

Simplelrucache는 TTL 지원을 통해 매우 간단하고 분산되지 않은 LRU 캐싱을 제공합니다. 두 가지 구현을 제공합니다.

  • 동시에 동시에 동시에
  • LinkedHashmap을 기반으로 동기화되었습니다

여기에서 찾을 수 있습니다. http://code.google.com/p/simplelrucache/

Java 코드를 사용하여 더 나은 LRU 캐시를 찾고 있습니다. Java LRU 캐시 코드를 사용하여 공유 할 수 있습니까? LinkedHashMap 그리고 Collections#synchronizedMap? 현재 사용 중입니다 LRUMap implements Map 그리고 코드는 잘 작동하지만 받고 있습니다 ArrayIndexOutofBoundException 아래 방법에서 500 명의 사용자를 사용하여로드 테스트시. 이 메소드는 최근 객체를 큐 앞쪽으로 이동합니다.

private void moveToFront(int index) {
        if (listHead != index) {
            int thisNext = nextElement[index];
            int thisPrev = prevElement[index];
            nextElement[thisPrev] = thisNext;
            if (thisNext >= 0) {
                prevElement[thisNext] = thisPrev;
            } else {
                listTail = thisPrev;
            }
            //old listHead and new listHead say new is 1 and old was 0 then prev[1]= 1 is the head now so no previ so -1
            // prev[0 old head] = new head right ; next[new head] = old head
            prevElement[index] = -1;
            nextElement[index] = listHead;
            prevElement[listHead] = index;
            listHead = index;
        }
    }

get(Object key) 그리고 put(Object key, Object value) 방법은 위를 호출합니다 moveToFront 방법.

행크의 답변에 의견을 추가하고 싶었지만 내가 할 수없는 방법 - 댓글로 취급하십시오.

LinkedHashMap은 생성자에 전달 된 매개 변수를 기반으로 액세스 순서를 유지합니다. 순서를 유지하기 위해 이중 정렬 목록을 유지합니다 (LinkedHashMap.entry 참조)

@pacerier 링크드 하슈 맵은 요소가 다시 추가되면 반복하는 동안 동일한 순서를 유지하지만 삽입 순서 모드의 경우에만 맞습니다.

이것이 내가 LinkedHashMap.entry 객체의 Java Docs에서 찾은 것입니다.

    /**
     * This method is invoked by the superclass whenever the value
     * of a pre-existing entry is read by Map.get or modified by Map.set.
     * If the enclosing Map is access-ordered, it moves the entry
     * to the end of the list; otherwise, it does nothing.
     */
    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();
            addBefore(lm.header);
        }
    }

이 방법은 최근에 액세스 한 요소를 목록의 끝으로 이동하는 것을 처리합니다. 따라서 모든 Linkedhashmap의 모든 것은 Lrucache를 구현하기위한 최고의 데이터 구조입니다.

Java의 Linkedhashmap 컬렉션을 사용한 또 다른 생각과 간단한 구현.

LinkedHashMap 제공 메소드 removeEldestentry와 예제에 언급 된 방식으로 무시할 수 있습니다. 기본적 으로이 수집 구조의 구현은 False입니다. 이 구조의 진실과 크기가 가장 오래된 요소보다 초기 용량을 넘어서면 제거됩니다.

제 경우에는 pageno와 페이지 컨텐츠를 가질 수 있습니다. Pageno는 정수이고 PageContent는 페이지 번호 값 문자열을 유지했습니다.

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

/**
 * @author Deepak Singhvi
 *
 */
public class LRUCacheUsingLinkedHashMap {


     private static int CACHE_SIZE = 3;
     public static void main(String[] args) {
        System.out.println(" Pages for consideration : 2, 1, 0, 2, 8, 2, 4,99");
        System.out.println("----------------------------------------------\n");


// accessOrder is true, so whenever any page gets changed or accessed,    // its order will change in the map, 
              LinkedHashMap<Integer,String> lruCache = new              
                 LinkedHashMap<Integer,String>(CACHE_SIZE, .75F, true) {

           private static final long serialVersionUID = 1L;

           protected boolean removeEldestEntry(Map.Entry<Integer,String>                           

                     eldest) {
                          return size() > CACHE_SIZE;
                     }

                };

  lruCache.put(2, "2");
  lruCache.put(1, "1");
  lruCache.put(0, "0");
  System.out.println(lruCache + "  , After first 3 pages in cache");
  lruCache.put(2, "2");
  System.out.println(lruCache + "  , Page 2 became the latest page in the cache");
  lruCache.put(8, "8");
  System.out.println(lruCache + "  , Adding page 8, which removes eldest element 2 ");
  lruCache.put(2, "2");
  System.out.println(lruCache+ "  , Page 2 became the latest page in the cache");
  lruCache.put(4, "4");
  System.out.println(lruCache+ "  , Adding page 4, which removes eldest element 1 ");
  lruCache.put(99, "99");
  System.out.println(lruCache + " , Adding page 99, which removes eldest element 8 ");

     }

}

위의 코드 실행 결과는 다음과 같습니다.

 Pages for consideration : 2, 1, 0, 2, 8, 2, 4,99
--------------------------------------------------
    {2=2, 1=1, 0=0}  , After first 3 pages in cache
    {2=2, 1=1, 0=0}  , Page 2 became the latest page in the cache
    {1=1, 0=0, 8=8}  , Adding page 8, which removes eldest element 2 
    {0=0, 8=8, 2=2}  , Page 2 became the latest page in the cache
    {8=8, 2=2, 4=4}  , Adding page 4, which removes eldest element 1 
    {2=2, 4=4, 99=99} , Adding page 99, which removes eldest element 8 

@Sanjanab 개념 (그러나 수정 후)에 이어 Lrucache 버전을 만들어 필요한 경우 제거 된 품목으로 무언가를 할 수있는 소비자도 제공했습니다.

public class LRUCache<K, V> {

    private ConcurrentHashMap<K, V> map;
    private final Consumer<V> onRemove;
    private ConcurrentLinkedQueue<K> queue;
    private final int size;

    public LRUCache(int size, Consumer<V> onRemove) {
        this.size = size;
        this.onRemove = onRemove;
        this.map = new ConcurrentHashMap<>(size);
        this.queue = new ConcurrentLinkedQueue<>();
    }

    public V get(K key) {
        //Recently accessed, hence move it to the tail
        if (queue.remove(key)) {
            queue.add(key);
            return map.get(key);
        }
        return null;
    }

    public void put(K key, V value) {
        //ConcurrentHashMap doesn't allow null key or values
        if (key == null || value == null) throw new IllegalArgumentException("key and value cannot be null!");

        V existing = map.get(key);
        if (existing != null) {
            queue.remove(key);
            onRemove.accept(existing);
        }

        if (map.size() >= size) {
            K lruKey = queue.poll();
            if (lruKey != null) {
                V removed = map.remove(lruKey);
                onRemove.accept(removed);
            }
        }
        queue.add(key);
        map.put(key, value);
    }
}

Android는 AN의 구현을 제공합니다 LRU 캐시. 그만큼 암호 깨끗하고 간단합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top