Синхронизация не совсем работает - PullRequest
1 голос
/ 15 марта 2012

У меня есть следующий код, который я пытаюсь написать в LRU Cache. У меня есть класс бегуна, который я использую для произвольной емкости кеша. Однако размер кэша превышает его емкость. Когда я синхронизирую метод FixLRU, он становится более точным, когда размер кеша больше 100, но становится медленнее. Когда я удаляю синхронизированное ключевое слово, кэш становится менее точным.

Есть идеи, как заставить это работать должным образом? более точным?

import java.util.concurrent.ConcurrentHashMap;

public abstract class Cache<TKey, TValue> implements ICache<TKey,TValue>{

    private final ConcurrentHashMap<TKey,TValue> _cache;

    protected Cache()
    {
        _cache=  new ConcurrentHashMap<TKey, TValue>();
    }

    protected Cache(int capacity){
        _cache = new ConcurrentHashMap<TKey, TValue>(capacity);
    }

    @Override
    public void Put(TKey key, TValue value) {
        _cache.put(key, value);
    }

    @Override
    public TValue Get(TKey key) {
        TValue value = _cache.get(key);

        return value;
    }

    @Override
    public void Delete(TKey key) {
        _cache.remove(key);
    }

    @Override
    public void Purge() {
        for(TKey key : _cache.keySet()){
            _cache.remove(key);
        }
    }

    public void IterateCache(){

        for(TKey key: _cache.keySet()){
            System.out.println("key:"+key+" , value:"+_cache.get(key));
        }

    }

    public int Count()
    {
        return _cache.size();
    }


}


import java.util.concurrent.ConcurrentLinkedQueue;

public class LRUCache<TKey,TValue> extends Cache<TKey,TValue> implements ICache<TKey, TValue> {

    private ConcurrentLinkedQueue<TKey> _queue;
    private int capacity;
    public LRUCache(){
        _queue = new ConcurrentLinkedQueue<TKey>();
    }

    public LRUCache(int capacity){
        this();
        this.capacity = capacity;
    }

    public void Put(TKey key, TValue value)
    { 
        FixLRU(key);

        super.Put(key, value);  
    }

    private void FixLRU(TKey key)
    {
        if(_queue.contains(key))
        {
            _queue.remove(key);
            super.Delete(key);
        }

        _queue.offer(key);

        while(_queue.size() > capacity){
            TKey keytoRemove =_queue.poll();
            super.Delete(keytoRemove);
        }
    }

    public TValue Get(TKey key){

        TValue _value = super.Get(key);

        if(_value == null){
            return null;
        }

        FixLRU(key);

        return _value;
    }

    public void Delete(TKey key){

        super.Delete(key);
    }

}

public class RunningLRU extends Thread{

    static LRUCache<String, String> cache = new LRUCache<String, String>(50);

    public static void main(String [ ] args) throws InterruptedException{

        Thread t1 = new RunningLRU();
        t1.start();
        Thread t2 = new RunningLRU();
        t2.start();
        Thread t3 = new RunningLRU();
        t3.start();
        Thread t4 = new RunningLRU();
        t4.start();
        try {
            t1.join();
            t2.join();
            t3.join();
            t4.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println(cache.toString());
        cache.IterateCache();
        System.out.println(cache.Count());

    }

    @Override
    public void run() {
        for(int i=0;i<100000;i++)
            cache.Put("test"+i, "test"+i);
    }

}

Ответы [ 2 ]

2 голосов
/ 15 марта 2012

Ваша проблема в том, что вы не используете специальную синхронизированную версию метода put putIfAbsent().Если вы не используете его, ConcurrentHashMap ведет себя так, как если бы не синхронизировалось - как карта нормалей, например HashMap.

Когда вы используете ее, вы должны продолжать использовать только вернул значение, поэтому ваш метод Put() не имеет правильной подписи (он должен возвращать TValue) для поддержки параллелизма.Вам потребуется изменить дизайн интерфейса.

Кроме того, в отличие от .Net land, в java land мы называем наши методы строчными, например put(), а не Put().Вам следует переименовать свои методы таким образом.

2 голосов
/ 15 марта 2012

Я бы убрал дополнительные записи после добавления вашей записи. Это минимизирует время, когда кеш будет больше, чем вы хотели. Вы также можете вызвать size () для выполнения очистки.

Есть идеи, как правильно это сделать?

Отражает ли ваш тест поведение вашего приложения? Может случиться так, что кеш ведет себя должным образом (или намного ближе к нему), когда вы его не забиваете. ;)

Если этот тест отражает поведение вашего приложения, то, возможно, LRUCache - не лучший выбор.

...