3

リンクされたハッシュ マップをサブクラス化して lru マップを作成しようとしています。

マップは collections.synchronized を介して実行されます。

マップのすべての使用箇所は、同期ブロックで囲まれています。それらがすべて削除された場合、単体テストも失敗します。マップは collections.synchronized を介して実行されたので、これらは必要ないと考える人もいるでしょう。

1 つのスレッドが連続番号 (0、1、2、3 ...) をマップに入れます。削除は、削除された最も古いエントリによって処理されます。誰もマップからエントリを削除しません。

もう一方のスレッドはマップからデータを取得します。

次の単体テストは、通常「oops」で失敗します。これは、ゼロ以外の数値が最初の位置に表示される場合です (マップがいっぱいになるまでゼロである必要があります)。エントリ セット内の null 値など、他の奇妙なことが発生する可能性があります。

任意のポインタをいただければ幸いです。

ありがとう

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);
}
4

1 に答える 1

1

ブロック内のコレクションにアクセスしている場合は、synchronized(lruMap)おそらくそれをラップしたくないでしょうCollections.synchronizedMap()-どちらか一方を使用してください。これは、おそらく異なるロックを使用するためです。実際、内部synchronizedMap()で使用される可能性は非常に低いため、ほぼ確実です。synchronized(this)

また、ここにリンクの説明を入力することをお勧めします

于 2012-10-31T15:11:24.657 に答える