You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生产者消费者实现中列表超容及索引越界问题求助

问题原因分析

1. 货架偶发超容的核心原因

你搞反了Semaphore的释放逻辑,这是超容的主要诱因。正确的生产者消费者Semaphore分工应该是:

  • 生产者持有的Semaphore(代表空货架数量),初始值设为maxCapacity(5),生产者获取许可后,这个许可要由消费者释放——消费者拿走商品空出位置后,把许可还给生产者,才能让新的生产者继续添加。
  • 消费者持有的Semaphore(代表可消费商品数量),初始值设为0,消费者获取许可后,这个许可要由生产者释放——生产者添加商品后,增加可消费数量,让消费者能获取许可取货。

如果你的代码里,生产者添加商品后错误地把许可还给了自己的Semaphore,而不是消费者的,那生产者的Semaphore许可数永远不会真正减少:比如初始5个许可,一个生产者acquire后变成4,添加完又release变回5,相当于许可被重复利用,后续生产者可以不断获取许可添加商品,直接导致货架size超过上限。

退一步说,就算Semaphore逻辑没错,普通List的size()判断和add()操作不是原子的。比如货架当前size是4,两个生产者线程同时通过Semaphore校验(拿到许可),其中一个线程还没完成add操作,另一个线程已经读取到过期的size=4值,接着执行add,最终货架size就会变成6——因为List的size变量本身不是线程安全的,多线程下读取的数值可能和实际情况不符。

2. IndexOutOfBoundsException的原因

普通List是非线程安全的,没有同步机制:

  • 多个消费者线程同时执行remove操作时会出现竞态条件:比如货架有1个商品,第一个消费者已经remove掉,size变成0,但第二个消费者已经读取到size=1的过期值,仍尝试remove(0),直接抛出索引越界异常。
  • 甚至List内部的数组结构可能因为多线程并发修改被损坏,导致size计数失真,进一步触发异常。
解决方案
  1. 修正Semaphore的使用逻辑
    严格遵循生产者消费者的Semaphore分工:

    • 生产者:先获取空货架许可(emptySemaphore.acquire()),添加商品后,释放可消费许可(fullSemaphore.release())。
    • 消费者:先获取可消费许可(fullSemaphore.acquire()),移除商品后,释放空货架许可(emptySemaphore.release())。
  2. 替换为线程安全的容器实现
    把普通List换成ConcurrentLinkedQueue(如果货架是队列结构,生产者从尾部加、消费者从头部取,天然适配生产者消费者模型),或者CopyOnWriteArrayList(适合读多写少的场景)。

  3. 可选:用ReentrantLock加锁
    如果需要对货架操作做更精细的控制,可以给Store类的add/remove方法加上ReentrantLock,保证每次只有一个线程能修改货架,彻底避免竞态条件。

修正后的核心代码示例

// Store类
public class Store {
    private final int maxShelfs;
    private final Queue<Object> shelf = new ConcurrentLinkedQueue<>();

    public Store(int maxShelfs) {
        this.maxShelfs = maxShelfs;
    }

    public boolean addGoods(Object goods) {
        if (shelf.size() >= maxShelfs) {
            return false;
        }
        return shelf.offer(goods);
    }

    public Object removeGoods() {
        return shelf.poll();
    }
}

// 生产者线程
class Producer implements Runnable {
    private final Store store;
    private final Semaphore emptySemaphore;
    private final Semaphore fullSemaphore;

    public Producer(Store store, Semaphore emptySemaphore, Semaphore fullSemaphore) {
        this.store = store;
        this.emptySemaphore = emptySemaphore;
        this.fullSemaphore = fullSemaphore;
    }

    @Override
    public void run() {
        while (true) {
            try {
                emptySemaphore.acquire();
                Object goods = new Object(); // 模拟生成商品
                if (store.addGoods(goods)) {
                    System.out.println("生产者添加商品,当前货架数量:" + store.shelf.size());
                    fullSemaphore.release();
                }
                Thread.sleep(100); // 模拟生产耗时
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

// 消费者线程
class Consumer implements Runnable {
    private final Store store;
    private final Semaphore emptySemaphore;
    private final Semaphore fullSemaphore;

    public Consumer(Store store, Semaphore emptySemaphore, Semaphore fullSemaphore) {
        this.store = store;
        this.emptySemaphore = emptySemaphore;
        this.fullSemaphore = fullSemaphore;
    }

    @Override
    public void run() {
        while (true) {
            try {
                fullSemaphore.acquire();
                Object goods = store.removeGoods();
                if (goods != null) {
                    System.out.println("消费者移除商品,当前货架数量:" + store.shelf.size());
                    emptySemaphore.release();
                }
                Thread.sleep(200); // 模拟消费耗时
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者ReD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:17:01