1

一般信息: 三个读取器线程以块的形式从文件中随机读取,其中每个块都有一个 ID,并且它们写入普通的 ArrayList。只要将具有所需 ID 的块添加到列表中,写入器线程就会写入输出文件。

出于这个原因,我编写了一个 BlockingChunkList,它应该同步 add() 和 getNextChunk() 方法。

它适用于我在一种情况下使用同步 + 通知 + notifyAll 和在另一种情况下使用同步列表。

当我使用 ReentrantLock 和 Condition 时,我无法做到这一点。writer-thread 只写了四个块,然后他就卡住了。

为什么它可能不起作用: 我怀疑一旦阅读器完成,作者就无法取回锁。但是我希望每次有东西要写(available = true)时,都应该调用写线程。当 available 为真时,他们似乎忽略了 hasAccess.await() 。

它应该如何工作: 读取线程仅调用 add 方法,并且仅在有要写入的内容(可用)时才释放写入线程。当 available=true 时,它​​们也会阻止自己。当作者通过调用 hasAccess.signalAll() 写了一些东西时,这个锁被释放。写线程只调用 getNextChunk() 方法,他在写块时释放其他线程。当 available=false 时,他会阻止自己,并被读者释放。

问题: 读取线程完成工作,写入线程只写入前 4 个块。我希望编写器总是在available=true 时被调用。

我不需要一个确切的解决方案,因为我认为我遗漏了一些东西,所以也很感激提示。所以:我错过了什么?

谢谢你

编辑:并发仅在发布的类中处理。主要方法仅启动踏板。编辑 2:这是我第一次尝试并发。我知道 ArrayList 不是线程安全的。我想通过使用 ReentrantLock 和 Condition 来理解这些概念。BASIC的想法是阻止读取器或写入器,无论可用是真还是假。

import java.util.ArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

    public class BlockingChunkQueue {

        private final ArrayList<Chunk> chunks;
        private volatile int currentID = 0; // first ID for which the writer needs to wait
        private volatile boolean available; // true if the ID is in the list

        private final Lock listLock;
        private final Condition hasAccess;

        public BlockingChunkQueue(){ 
            chunks = new ArrayList<>(); 
            listLock = new ReentrantLock();
            hasAccess = listLock.newCondition();
            available = false;
        }

        /*
         * While there is a chunk which might be written to the output (=correct ID) then wait for access.
         * Then add the chunk and if the added chunk has the ID for which the other thread is waiting, then,
         * then available = true and signal the waiting thread.
         * 
         * 
         */
        public void add(Chunk chunk) throws InterruptedException{
            listLock.lock();
            try{
                while(available){
                    hasAccess.await(); // reader block yourself until you get the lock back
                }
                chunks.add(chunk);

            if(chunk.getId() == currentID){
                available = true;
                hasAccess.signalAll();
            }
            }finally{
                listLock.unlock();
            }
        }

        /*
         * If the chunk is not available then wait.
         * If it becomes available then increment the ID, remove it from the list, signal the reader-threads and set available false.
         * return the chunk which will be written to the output.
         */
        public Chunk getNextChunk() throws InterruptedException {
            listLock.lock();
            try{
                while(!available){
                    hasAccess.await(); // block yourself until u can write something
                }

                for(Chunk c : chunks){
                    if(c.getId() == currentID){
                        currentID++;
                        chunks.remove(c);
                        hasAccess.signalAll(); //signal the readers
                        available = false;
                        return c;
                    }
                }

            }finally{
                listLock.unlock();
            }
            return null;
        }

        public int getcurrentID(){ return currentID;}

        public boolean isEmpty(){ return chunks.isEmpty(); }

        public int size(){return chunks.size();}
    }

解决方案: 处理线程没有任何问题。事实证明这是我这边的一个逻辑错误。写入线程卡住了,因为他没有机会检查必要的 ID,因为写入者随机读取块。感谢您的帮助。

4

1 回答 1

2

这里有几个问题。

available仅在持有时读取或变异的 volatile 变量的目的lock是什么?

isEmtpyandsize方法在chunks不持有.的情况下调用方法lockArrayList不是线程安全的。这些调用的行为无法预测。

您可能会卡住的一个原因是如果在调用 getNextChunk 之前添加了多个块。

在您的循环中查找您设置为 false 的“当前”,但它实际上可能已经在列表中:

            for(Chunk c : chunks){
                if(c.getId() == currentID){
                    currentID++;
                    chunks.remove(c);
                    hasAccess.signalAll(); //signal the readers
                    available = false;
                    return c;
                }
            }

考虑将块存储在 a 中Map<Integer,Chunk>,以便可以通过标识符轻松查看块是否存在。

于 2015-04-18T17:03:45.857 回答