Как правильно использовать синхронизированную связанную хэш-карту

Пытаясь создать карту lru путем создания подклассов связанной хэш-карты.

карта запускается через collection.synchronized.

все виды использования карты окружены синхронизированным блоком. модульный тест также не проходит, если все они удалены. можно было бы подумать, что они не нужны, так как карта была запущена с использованием collection.synchronized.

один поток помещает в карту последовательные числа (0,1,2,3 ...). удаления обрабатываются удаленным старшим входом. никто не удаляет записи с карты.

другой поток получает данные с карты.

следующий юнит-тест не проходит обычно при "упс". это когда ненулевое число появляется в первой позиции (оно должно быть нулевым, пока карта не заполнится). другие странные вещи могут происходить как нулевые значения в наборе записей.

любые указатели будут оценены.

Спасибо

import static org.junit.Assert.*;

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
class LruMap<K,V> extends LinkedHashMap<K,V> {
    public LruMap() {
        super(defaultMaxSize+1,.75f,true);
        maxSize=defaultMaxSize;
    }
    public LruMap(int arg0) {
        super(arg0+1,.75f,true);
        maxSize=arg0;
    }
    public LruMap(int arg0,float arg1) {
        super(arg0+1,arg1,true);
        maxSize=arg0;
    }
    public LruMap(int arg0,float arg1,boolean arg2) {
        super(arg0+1,arg1,arg2);
        if(!arg2)
            throw new RuntimeException("you did not construct an lru map!");
        maxSize=arg0;
    }
    public LruMap(Map<K,V> arg0) {
        super(arg0);
        throw new RuntimeException("you did not construct an lru map!");
    }
    public boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return size()>maxSize;
    }
    public final int maxSize;
    public static final int defaultMaxSize=2048;
    static final long serialVersionUID=0;
}
class Server implements Runnable {
    public Server(final int pieces,final int period) {
        this.pieces=pieces;
        this.period=period;
        lruMap=Collections.synchronizedMap(new LruMap<Long,Long>(3*pieces/2));
    }
    @Override public void run() {
        t0=System.currentTimeMillis();
        while(piece<stopAtPiece) {
            final long dt=System.currentTimeMillis()-t0;
            final long target=piece(dt);
            System.out.println("adding "+(target-piece+1)+" items");
            for(;piece<=target;piece++) {
                synchronized(lruMap) {
                    lruMap.put(piece,piece);
                }
            }
            checkMap(piece,true);
            try {
                Thread.sleep(100);
            } catch(InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
    Map.Entry<Long,Long>[] checkMap(final long n,boolean print) {
        synchronized(lruMap) {
            Map.Entry<Long,Long>[] entries=null;
            if(lruMap.size()>0) {
                final Set<Map.Entry<Long,Long>> entrySet=lruMap.entrySet();
                entries=new Map.Entry[entrySet.size()];
                entrySet.toArray(entries);
                long first=entries[0].getKey();
                long last=entries[entries.length-1].getKey();
                if(print)
                    for(Map.Entry<Long,Long> entry:entries)
                        System.out.print(entry.getKey()+" ");
                System.out.println();
                if(n<pieces&&first!=0) {
                    System.out.println("lru: first!=0! "+first);
                    if(throwWhenfirstIsNotZero) { throw new RuntimeException("oops"); }
                }
                for(int i=0;i<entries.length-1;i++) {
                    long p0=entries[i].getKey();
                    long p1=entries[i+1].getKey();
                    if(p0>p1)
                        System.out.println("out of order! "+p0+" "+p1);
                    else if(p0==p1)
                        System.out.println("dupicate "+p0+" "+p1);
                    else if(p0+1==p1)
                        ; // ok
                    else if(p0+1<p1)
                        System.out.println("skipped "+p0+" "+p1);
                    else System.out.println("some case i mssed!");
                }
            }
            return entries;
        }
    }
    public long piece(final long dt) {
        return dt/period*pieces+dt%period*pieces/period;
    }
    public boolean throwWhenfirstIsNotZero=true;
    protected long piece;
    public long t0;
    protected long stopAtPiece=Long.MAX_VALUE;
    public final int period;
    public final int pieces;
    public final Map<Long,Long> lruMap;
}
public class ServerTestCase {
    @Before public void setUp() throws Exception {}
    @After public void tearDown() throws Exception {}
    @Test public void testRun() {
        server.stopAtPiece=server.pieces;
        server.throwWhenfirstIsNotZero=true;
        Thread thread=new Thread(server);
        thread.setName("server");
        thread.start();
        while(thread.isAlive()) {
            for(long i=0;i<server.piece;i++)
                synchronized(server.lruMap) {
                    server.lruMap.get(i);
                }
        }
    }
    final int period=2*1000;
    final int serverPieces=100;
    Server server=new Server(serverPieces,period);
}

1 ответ

Если вы получаете доступ к коллекции внутри synchronized(lruMap) блок, то вы, вероятно, не хотите, чтобы обернуть его Collections.synchronizedMap() - использовать один или другой. Это потому, что они, вероятно, будут использовать разные блокировки - на самом деле это почти наверняка, потому что крайне маловероятно, что synchronizedMap() использует synchronized(this) внутренне.

Также я рекомендую ввести описание ссылки здесь

Другие вопросы по тегам