2

同時スキップリストマップを使用しようとしています。同期されたリンクされたハッシュマップを正しく使用する方法に問題があったので、同時スキップリストマップを試してみることにしました。

私も同じような問題を抱えています。以下の単体テストは失敗します。これは、エントリセットを取得したときに、size()がマップが空でないことを示している場合にnull値が含まれるためです。naict、私は同期されたマップへのすべてのアクセス権を持っています。

これは並行マップなので、これを(同期して)行う必要はないと思います。

サーバーは、数値0、1、2、3、...をマップに配置し、サイズをしきい値未満に保ちます。サーバーが起動してから経過したミリ秒ごとに1つの数値を入力しようとします。

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

ありがとう

import static org.junit.Assert.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListMap;
import org.junit.*;
class DummyServer implements Runnable {
    DummyServer(int pieces) {
        t0=System.currentTimeMillis();
        this.pieces=pieces;
        max=pieces;
        lruMap=new ConcurrentSkipListMap<Long,Long>();
    }
    Set<Map.Entry<Long,Long>> entrySet() {
        Set<Entry<Long,Long>> entries=null;
        synchronized(lruMap) {
            entries=Collections.unmodifiableSet(lruMap.entrySet());
        }
        return entries;
    }
    Set<Long> keySet() {
        Set<Long> entries=null;
        synchronized(lruMap) {
            entries=Collections.unmodifiableSet(lruMap.keySet());
        }
        return entries;
    }
    @Override public void run() {
        int n=0;
        while(piece<stopAtPiece) {
            long target=piece(System.currentTimeMillis()-t0);
            long n0=piece;
            for(;piece<target;piece++,n++)
                put(piece);
            if(n>max+max/10) {
                Long[] keys=keySet().toArray(new Long[0]);
                synchronized(lruMap) {
                    for(int i=0;n>max;i++,n--)
                        lruMap.remove(keys[i]);
                }
            }
            try {
                Thread.sleep(10);
            } catch(InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
    private void put(long piece) {
        synchronized(lruMap) {
            lruMap.put(piece,piece);
        }
    }
    public long piece() {
        return piece;
    }
    public Long get(long piece) {
        synchronized(lruMap) {
            return lruMap.get(piece);
        }
    }
    public int size() {
        synchronized(lruMap) {
            return lruMap.size();
        }
    }
    public long piece(long dt) {
        return dt/period*pieces+dt%period*pieces/period;
    }
    private long piece;
    int period=2000;
    private volatile Map<Long,Long> lruMap;
    public final long t0;
    protected final int pieces;
    public final int max;
    public long stopAtPiece=Long.MAX_VALUE;
}
public class DummyServerTestCase {
    void checkMap(Long n) {
        if(server.size()>0) {
            final Set<Map.Entry<Long,Long>> mapValues=server.entrySet();
            @SuppressWarnings("unchecked") final Map.Entry<Long,Long>[] entries=new Map.Entry[mapValues.size()];
            mapValues.toArray(entries);
            try {
                if(entries[0]==null)
                    System.out.println(server.piece());
                assertNotNull(entries[0]);
            } catch(Exception e) {
                fail(e.toString());
            }
        }
    }
    @Test public void testRunForFirstIsNotZero() {
        server.stopAtPiece=1*server.pieces;
        Thread thread=new Thread(server);
        thread.start();
        while(thread.isAlive()) {
            for(long i=0;i<server.piece();i++) {
                server.get(i);
                Thread.yield();
                checkMap(server.piece());
                Thread.yield();
            }
        }
    }
    DummyServer server=new DummyServer(1000);
}
4

2 に答える 2

2

問題は、あなたが実行していることです

final Map.Entry<Long,Long>[] entries=new Map.Entry[mapValues.size()]; // size>0
mapValues.toArray(entries); // size is 0.

配列を作成してから toArray を呼び出すまでの間に、マップをクリアしています。

Iterator を使用してコピーを取得すると、この競合状態は発生しません。

void checkMap(Long n) {
    final Set<Map.Entry<Long, Long>> mapValues = server.entrySet();
    Set<Map.Entry<Long, Long>> entries = new LinkedHashSet<>(mapValues);

    for (Entry<Long, Long> entry : entries) {
        assertNotNull(entry);
    }
}

また

void checkMap(Long n) {
    for (Entry<Long, Long> entry : server.entrySet())
        assertNotNull(entry);
}
于 2012-08-13T15:32:29.753 に答える
2

まずsynchronize、複合操作を行う必要がない限り、スレッドセーフなコレクションの実装は必要ありません。はConcurrentMap優れた原子複合関数を提供するので、そうする必要はありません。

2番。size並行操作を行っている間は、メソッドが正しいことに依存するべきではありません。javadoc には次のように記載されています。

ほとんどのコレクションとは異なり、size メソッドは一定時間の操作ではないことに注意してください。これらのマップは非同期であるため、現在の要素数を判断するには、要素を走査する必要があります。

サイズは、呼び出しを開始してからリターンを取得するまでに異なる場合があります。

つまり、テストは有効な同時テストではありません。あなたが達成しようとしていることについてもっと詳しく説明できますか?

于 2012-08-13T15:33:47.543 に答える