java - 如何对有界缓冲区实现同步检查以避免竞争条件?

标签 java multithreading concurrency race-condition producer-consumer

在处理经典的多消费者/生产者问题时,我遇到了一个让我陷入困境的问题,即如何在从循环缓冲区插入/删除时避免竞争条件。提前感谢任何帮助!

用于示例目的的循环缓冲区的示例代码。与我的实现类似(注意:我不能使用集合类型,只能使用数组):

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

public class BoundedBuffer {
    private final String[] buffer;
    private final int capacity;

    private int front;
    private int rear;
    private int count;

    private final Lock lock = new ReentrantLock();

    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    public BoundedBuffer(int capacity) {
        super();

        this.capacity = capacity;

        buffer = new String[capacity];
    }

    public void deposit(String data) throws InterruptedException {
        lock.lock();

        try {
            while (count == capacity) {
                notFull.await();
            }

            buffer[rear] = data;
            rear = (rear + 1) % capacity;
            count++;

            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public String fetch() throws InterruptedException {
        lock.lock();

        try {
            while (count == 0) {
                notEmpty.await();
            }

            String result = buffer[front];
            front = (front + 1) % capacity;
            count--;

            notFull.signal();

            return result;
        } finally {
            lock.unlock();
        }
    }
}

我需要知道的是如何实现检查缓冲区是否已满/空的方法?此方法需要包含在此 BoundedBuffer 中,并且必须在继续/调用插入/写入方法之前从另一个类(生产者/消费者)调用。

Producer 类中方法的伪代码。

    if (!bufferFull) {
        buffer.addelement;
    }

    else {
        thread.sleep(5)
        threadHasSleptFor++;
    }

我正在使用线程,并且有多个生产者/消费者(在本例中是 2 个生产者/消费者,但我可能需要更多)。我需要它,以便如果缓冲区已满,线程必须等待,直到它可用于插入,并且需要存储它等待的时间以用于输出目的(不是调试,核心功能的一部分)。我遇到的问题是:

  1. 线程 1 生产者检查缓冲区已满的情况,这是错误的。
  2. 调度程序中途切换到线程 2。
  3. 线程 2 还检查 bufferfull 条件,但结果为 false。
  4. 线程 2 继续插入。
  5. 调度程序切换回线程 1。
  6. 线程 1 现在进入插入行,因为它已经检查过,但线程 2 击败了它。
  7. 繁荣。

对于 Java 来说有点新,但据我了解,这是“检查时间/使用时间”竞争条件问题。

有人可以建议如何安全地实现这一点,以及如何循环代码,以便 threadHasSleptFor 变量在每次失败时不断递增(提供方法就太好了)。我希望只有请求检查的线程才能开始插入项目;第二个生产者必须等待锁定。

谢谢。

最佳答案

根据定义,如果没有更高级别的锁定,这是不可能做到的。

您必须保证缓冲区是否已满的检查以及接下来的插入从线程的角度来看是原子的,这意味着您必须获取一些公共(public)锁才能执行此操作。这个普遍问题确实叫Time of check to time to use并导致许多有趣的竞争条件。

解决这些问题的方法是不检查是否可以执行某个操作然后执行该操作,而只是尝试该操作并处理错误情况。因此,如果您不想在操作时缓冲区已满时阻塞,只需实现一个 tryDeposit 方法,该方法在无法存储值时抛出异常,或者返回 boolean 成功值。

尽管在您的情况下,如果您必须存储将值插入堆栈之前所需的时间,但我不明白为什么要简单:

long start = System.nanotime();
queue.deposit();
long end = System.nanotime();

也不会成功。

关于java - 如何对有界缓冲区实现同步检查以避免竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21518730/

相关文章:

java - 数据行与正则表达式模式不匹配

java - Android Facebook 登录 - 由 null 引起的错误

java - 流形和结构

python - 将线程同步责任移至共享资源中

concurrency - 选择正确的 Clojure 引用类型以协调写入/读取

java - Struts 2中如何处理不是来自JSP的Action表单数据?

ios - 如何在glkview中使用dispatch_async?

c++ - 在 main() 的线程中调用对象成员函数

c++ - 发布获取语义以计算平均值

java - StampedLock.writeLock() 是否释放当前线程持有的读锁?