如何正确使用同步链接哈希映射

时间:2012-08-03 08:37:45

标签: java map synchronized linkedhashmap

尝试通过子类化链接哈希映射来制作一个lru地图。

地图通过collections.synchronized运行。

地图的所有用法都被同步块包围。如果全部删除,单元测试也会失败。人们会认为它们不是必需的,因为地图是通过collections.synchronized运行的。

一个线程将连续数字(0,1,2,3 ...)放入地图中。删除由删除的最旧条目处理。没有人从地图中删除条目。

另一个线程从地图中获取数据。

以下单元测试通常在“oops”失败。这是当非零数字出现在第一个位置时(它应该为零,直到地图变满)。其他奇怪的事情可能发生在条目集中的空值。

任何指针都将受到赞赏。

感谢

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 个答案:

答案 0 :(得分:1)

如果您正在访问synchronized(lruMap)块内的集合,那么您可能不希望将其包装在Collections.synchronizedMap()中 - 使用其中一个。这是因为他们可能会使用不同的锁 - 事实上它几乎可以肯定,因为synchronizedMap()内部使用synchronized(this)的可能性极小。

我还建议enter link description here