Как правильно использовать concurrentskiplistmap?

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

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

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

сервер просто помещает числа 0,1,2,3, ... в карту, сохраняя его размер ниже порогового значения. он пытается ввести одно число для каждой миллисекунды, прошедшей с момента запуска сервера.

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

Спасибо

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

2 ответа

Решение

Проблема в том, что вы выполняете

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

Между созданием массива и вызовом toArray вы очищаете карту.

Если вы возьмете копию с помощью Итератора, вы не получите этого условия гонки.

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

Во-первых, вам никогда не придется synchronize потокобезопасная реализация коллекции, если вам не нужно выполнять какую-то составную операцию. ConcurrentMap предлагает хорошие функции атомного соединения для вас, так что даже тогда вы не должны.

Во-вторых. Вы никогда не должны полагаться на size Метод должен быть правильным при выполнении параллельных операций. Javadoc отмечает:

Помните, что, в отличие от большинства коллекций, метод size не является операцией с постоянным временем. Из-за асинхронного характера этих карт для определения текущего количества элементов требуется обход элементов.

Размер может отличаться от того, когда вы начинаете вызов, когда вы получаете возврат.

Короче говоря, ваш тест не является действительным параллельным тестом. Можете ли вы подробнее рассказать о том, что вы пытаетесь достичь?

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