为了账号安全,请及时绑定邮箱和手机立即绑定

对单个静态整数索引进行多次读写会导致索引越界

对单个静态整数索引进行多次读写会导致索引越界

富国沪深 2023-07-13 17:45:51
我试图学习多线程,并尝试使用等待和通知的简单生产者/消费者模式。当我将模式拆分为两个消耗和一个产生时,我得到一个 ArrayIndexOutOfBounds 异常,该异常尚不清楚。该问题并不总是发生,有时会发生。我使用的是 I3 处理器。我尝试添加一个 if 块来检查计数变量是否低于或高于声明的大小,但问题仍然存在。    private static Object key = new Object();    private static int[] buffer;    private volatile static Integer count;    static class Consumer {        void consume() {            synchronized (key) {                if (isEmpty()) {                    try {                        key.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                buffer[--count] = 0;                key.notify();            }        }        public boolean isEmpty() {            return count == 0;        }    }    static class Producer {        void produce() {            synchronized (key) {                if (isFull()) {                    try {                        key.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                buffer[count++] = 1;                key.notify();            }        }        public boolean isFull() {            return count == buffer.length;        }    }    public static void main(String[] args) throws InterruptedException {        buffer = new int[10];        count = 0;        Producer producer = new Producer();        Consumer consumer = new Consumer();        Runnable produce = () -> {            for (int i = 0; i < 1500; i++)                producer.produce();            System.out.println("Done producing");        };        Runnable consume = () -> {            for (int i = 0; i < 1300; i++)                consumer.consume();            System.out.println("Done consuming");        };        };
查看完整描述

2 回答

?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

问题是:

  • 对于“满”和“空”状态,您只有一个共享的等待条件

  • 您正在运行两个消费者线程(“consumerWorker”和“delayedConsumer”)和一个生产者线程(“ ProducerWorker”)

  • 你没有像你应该的那样重新检查 while 循环中的条件

可能发生的事情是:

  1. 两个消费者线程都看到缓冲区为空

  2. 生产者线程将一项放入缓冲区并发出通知。

  3. 单个消费者线程唤醒并处理一项。缓冲区现在是空的。然后它会发出通知。

  4. 第二个消费者线程醒来(而不是像您预期的那样的生产者),不检查缓冲区是否为空,并尝试访问索引 -1 处的缓冲区。

您从通话中醒来后需要重新检查情况wait。将 更改ifwhile. 以下是如何为消费者做到这一点;您需要为制作人做同样的事情。

    void consume() {

        synchronized (key) {

            while (isEmpty()) {

                try {

                    key.wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            buffer[--count] = 0;

            key.notify();

        }

    }

这样消费者就不会被来自notify其他消费者线程的 所迷惑。


查看完整回答
反对 回复 2023-07-13
?
函数式编程

TA贡献1807条经验 获得超9个赞

你的缓冲区长度为 10

buffer = new int[10];

因此,每当您的计数变量超过数组容量(即 10)时,就会很明显

    private volatile static Integer count;

你会得到ArrayIndexOutOfBoundsException

您生产更多,消耗更少,消耗更少,并且生产/消费的执行不是顺序的,它更像是循环。


查看完整回答
反对 回复 2023-07-13
  • 2 回答
  • 0 关注
  • 109 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信