具有concurrenthashmap和Java线程的生产者/消费者(类型)

时间:2018-02-13 18:33:31

标签: java multithreading concurrency concurrenthashmap

我有一个线程写入我的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();
    }
}
  • Thread1为blockstore提供了块,这些块在concurrenthashmap中编入索引。这些块是写入文件的实际byte []的抽象。在写入。
  • 之前,该块为空
  • Thread2尝试在索引i处获取一个块,如果它不存在,它应该等到它并检索它。线程2可以稍后再次访问同一个块,因此无法删除它。

E.g。鉴于以下情况

1:thread2.getBlock(0,emtpyBuff)//块不存在,因此线程等待
2. thread1.writeBlock(0,buffWithData)//将数据块写入文件,将块添加到hashmap 3:thread2被通知块现在存在,从hashmap中检索偏移量,将相应的文件块写入emptyBuff,然后继续执行操作。

这个和另一个问题的主要区别在于我不想直接返回hashmap的值,而是在blockstore类中对它执行进一步的操作以获取实际的文件数据。

谢谢! :)

编辑:

我考虑过简单地从thread2轮询blockstore,直到给出返回值,但这导致了大量不必要的CPU使用率。

1 个答案:

答案 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();
    }
}