Java ReentrantLock和条件生产者完成工作,消费者陷入困境 [英] Java ReentrantLock and Condition | producers finish work, consumer gets stuck

查看:138
本文介绍了Java ReentrantLock和条件生产者完成工作,消费者陷入困境的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

常规信息:
三个读取器线程从文件中随机读取数据块,每个数据块都有一个ID,它们分别写入普通的ArrayList。一旦将具有所需ID的块添加到列表中,编写器线程就会写入输出文件。

General Information: Three reader-threads read randomly from a file in chunks where each chunk has an ID and they write to a normal ArrayList. A writer-thread writes to an outputfile as soon as a chunk with the needed ID is added to the list.

由于这个原因,我编写了一个BlockingChunkList来同步add()和getNextChunk()方法。

For that reason I have written a BlockingChunkList which should synchronize the add() and getNextChunk() methods.

在一种情况下,它适用于我使用sync + notify + notifyAll,而在另一种情况下适用于同步列表。

It works for me with synchronized + notify + notifyAll in one case and with a synchronized list in another.

当我使用ReentrantLock and Condition时,我无法做到这一点。编写器线程仅写入四个块,然后卡住。

I don't manage to do it when I use ReentrantLock and Condition. The writer-thread only writes four chunks and then he gets stuck.

为什么可能不起作用:
我怀疑一旦读者完成,作家就不会再获得锁定。但是我希望每次有写东西(available = true)时都应该调用写线程。

Why it might not work: I have the suspicion that once the readers are done the writer doesn't get the lock back. However i would expect that everytime when there is something to write (available=true) then the writer thread should be called. They seem to ignore hasAccess.await() when available is true.

应如何工作:
阅读线程只调用它们,它们似乎忽略了hasAccess.await()。使用add方法,并且只有在有要写入的内容(可用)时,它们才会释放写入线程。当available = true时,它们也会阻止自己。当编写者通过调用hasAccess.signalAll()
进行写操作时,将释放此锁定。写线程仅调用getNextChunk()方法,并且在写块时释放其他线程。当available = false并且被读者释放时,他将自己封锁。

How it should work: The reading threads only call the add method and they release the writing thread only when there is something to write (available). They also block themselves when available=true. This lock is released when the writer has written something by calling hasAccess.signalAll() The writing thread only calls the getNextChunk() method and he releases the other threads when he wrote the chunk. He blocks himself when available=false and he is released by the readers.

问题:
阅读线程完成了他们的工作,并且写入线程仅写入前4个块。我希望在有可用= true时总是会叫作者。

Question: The reading threads finish their work and the writing thread only writes the first 4 chunks. I expect that the writer is always called when available=true.

我不需要确切的解决方案,因为我认为我缺少了一些东西,所以也很感谢。因此:我想念什么?

I don't need an exact solution, a hint is appreciated as well since I think I am missing something. So: What am I missing ?

谢谢

编辑:并发仅在发布的类中处理。主方法仅启动胎面。
编辑2:这是我对并发性的第一枪。我知道ArrayList不是线程安全的。我想通过使用ReentrantLock和Condition来做到这一点,以便理解这些概念。 BASIC 的想法是阻止读者或作家,无论可用的是对还是不对。

Concurrency is handeled only in the posted class. The main-method only starts the treads. EDIT 2: This is one of my first shots at concurrency. I know that ArrayList is not thread safe. I would like to make it so by using ReentrantLock and Condition in order to understand the concepts. The BASIC idea is to block either reader or writer whether available is true or false.

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,因为作家随机读取了这些块。

SOLUTION: There was nothing wrong with handling the threads. It turned out to be a logical error from my side. The writing thread gets stuck because he doesn't get the chance to check for the necessary IDs because the writers read the chunks randomly. Thanks for the helpfull answer.

推荐答案

这里有几个问题。

易失性变量可用的用途是什么?仅当持有时,该变量才被读取或突变。 ?

What is the purpose of the volatile variable available which is only read or mutated while the lock is held?

isEmtpy size 方法调用方法而不按住 ArrayList 不是线程安全的。这些调用的行为无法预测。

The isEmtpy and size methods call methods on chunks without holding the lock. ArrayList is not thread-safe. The behavior of these calls cannot be predicted.

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

A reason you might get stuck is if multiple chunks get added before getNextChunk is called.

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

In your loop to find the "current" you set available to false, but it may actually already be in the list:

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

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

Consider storing your chunks in a Map<Integer,Chunk> so that can easily see if a chunk is present by the identifier.

这篇关于Java ReentrantLock和条件生产者完成工作,消费者陷入困境的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆