论Object.wait()要放到while循环里

wait()放while里面算是一个常识性的准则。为什么要这样呢,如果放到if里面会有什么后果?今天水木有人贴出了一段出错的代码,对这个问题现身说法:

public class A {
        private Object[] queue = new Object[1024];
        private int cMsg;

        public synchronized boolean accept(Object msg, Object token)  {
                if (cMsg >= queue.length) {
                        try {
                                wait();
                        }
                        catch (InterruptedException e) {
                                return false;
                        }
                }

                queue[cMsg++] = token;
                queue[cMsg++] = msg;
                return true;
        }

        public synchronized Object[] getMessages()  {
                if (cMsg == 0) {
                        return null;
                }

                Object[] tmp = (Object[]) Arrays.copyOf(queue, cMsg);
                Arrays.fill(queue, 0, cMsg, null);
                cMsg = 0;
                notify();
                return tmp;
        }
}

这个代码在大并发下测试,抛出了java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 1025异常。

要分析原因的话,就是wait()被唤醒后,队列已经满了,cMsg >= queue.length这个条件已经不满足了,再往后移下标的话就数组越界了。

问题是为什么wait()唤醒后队列会满。在代码里,将队列清空后,才执行notify(),这个时候它应该只唤醒了一个线程,那么谁把队列填满的呢

答案是一个阻塞在accept上面的线程。首先要知道一点:其他线程收到信号并不是在notify调用的那一刻!notify的信号是在退出同步函数后才发出的,从退出同步函数,到信号发出,这中间有个时间差,因而就有可能出现以下执行序列:

1. getMessages方法中的 notify()调用
2. getMessages退出,此线程A释放类实例上的monitor
3. 一个阻塞在accept上的线程B,得以进入accept方法,因为此时数组被清空,线程B填入数据,下表+2;accept不断的调用,直到数组被填满,而阻塞在wait()调用上
4. notify信号发出,一个线程C被唤醒。这时没有再判断数组下标位置,直接想数组中塞数据,数组越界。

解决这个问题的方法当然就是把if改为while:

                while (cMsg >= queue.length) {
                        try {
                                wait();
                        }
                        catch (InterruptedException e) {
                                return false;
                        }
                }

在被唤醒后,重新判断条件是否满足。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值