尝试使用并发跳过列表映射。 我在如何正确使用同步链接哈希映射方面遇到了问题,所以我决定尝试一下并发跳过列表映射。
我也有同样的问题。 下面的单元测试失败,因为当我获得条目集时,当 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);
}
问题是你正在执行
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 方法不是 恒定时间操作。由于这些的异步性质 地图,确定当前元素的数量需要遍历 的元素。
大小可以不同于开始调用时到获取返回时的大小。
简而言之,您的测试不是有效的并发测试。 您能详细说明您要实现的目标吗?