我有一个线程写入我的concurrenthashmap,另一个线程读取这些块(不删除它们)。
一个类似的问题here几乎提供了解决方案,但并不完全适用于我的情况。
我有这堂课:
public class BlockStore {
private ConcurrentHashMap<Integer, Long> blockToOffset = new ConcurrentHashMap<>();
private RandomAccessFile randomAccessFile;
public BlockStore(RandomAccessFile randomAccessFile, long fileLength) throws IOException {
this.randomAccessFile = randomAccessFile;
randomAccessFile.setLength(fileLength);
}
public boolean hasBlock(int blockNumber) {
return blockToOffset.containsKey(blockNumber);
}
public void getBlock(int blockNumber, byte[] buffer) throws BlockNotFoundException, IOException {
if (hasBlock(blockNumber)) {
long offset = blockToOffset.get(blockNumber);
randomAccessFile.seek(offset);
randomAccessFile.readFully(buffer);
} else throw new BlockNotFoundException();
}
public void writeBlock(int blockNumber, byte[] buffer) throws IOException {
long offset = blockNumber * NodeUtil.FILE_BUFFER_SIZE;
randomAccessFile.seek(offset);
randomAccessFile.write(buffer);
blockToOffset.put(blockNumber, offset);
blockToOffset.notifyAll();
}
public boolean allFilesReceived() throws IOException {
double expectedNumberOfBlocks = Math.ceil(((double) randomAccessFile.length()/NodeUtil.FILE_BUFFER_SIZE));
return expectedNumberOfBlocks == blockToOffset.size();
}
}
E.g。鉴于以下情况
1:thread2.getBlock(0,emtpyBuff)//块不存在,因此线程等待
2. thread1.writeBlock(0,buffWithData)//将数据块写入文件,将块添加到hashmap
3:thread2被通知块现在存在,从hashmap中检索偏移量,将相应的文件块写入emptyBuff,然后继续执行操作。
这个和另一个问题的主要区别在于我不想直接返回hashmap的值,而是在blockstore类中对它执行进一步的操作以获取实际的文件数据。
谢谢! :)
编辑:
我考虑过简单地从thread2轮询blockstore,直到给出返回值,但这导致了大量不必要的CPU使用率。
答案 0 :(得分:0)
对于这个问题似乎没有一个“好的”解决方案,所以我调整了引用的问题以匹配我自己的问题。当一个线程从阻塞队列中读取时,这只是一个安全的解决方案,因为轮询然后getBlock()中的返回值不是线程安全的。
public class BlockStore {
private ConcurrentHashMap<Integer, BlockingQueue<Long>> blockToOffset = new ConcurrentHashMap<>();
private RandomAccessFile randomAccessFile;
public BlockStore(RandomAccessFile randomAccessFile, long fileLength) throws IOException {
this.randomAccessFile = randomAccessFile;
randomAccessFile.setLength(fileLength);
}
private synchronized BlockingQueue<Long> ensureQueueExists(int key) {
if (blockToOffset.containsKey(key)) {
return blockToOffset.get(key);
} else {
BlockingQueue<Long> queue = new ArrayBlockingQueue<>(1);
blockToOffset.put(key, queue);
return queue;
}
}
public void getBlock(int blockNumber, byte[] buffer) {
BlockingQueue<Long> queue = ensureQueueExists(blockNumber);
try {
long offset = queue.poll(60L, TimeUnit.SECONDS);
queue.add(offset); // Put offset back into queue. Since get will only be called by one thread, this does not result in a race condition
randomAccessFile.seek(offset);
randomAccessFile.readFully(buffer);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
public void writeBlock(int blockNumber, byte[] buffer) throws IOException {
BlockingQueue<Long> queue = ensureQueueExists(blockNumber);
long offset = blockNumber * NodeUtil.FILE_BUFFER_SIZE;
randomAccessFile.seek(offset);
randomAccessFile.write(buffer);
queue.add(offset);
blockToOffset.put(blockNumber, queue);
}
public boolean allFilesReceived() throws IOException {
double expectedNumberOfBlocks = Math.ceil(((double) randomAccessFile.length()/NodeUtil.FILE_BUFFER_SIZE));
return expectedNumberOfBlocks == blockToOffset.size();
}
}