JDK8 LinkedBlockingQueue遇到stream会出现死循环的情况

JDK8 LinkedBlockingQueue遇到stream会出现死循环的情况

测试用例

public class TestQueue {
    public static void main(String[] args) throws Exception {
        LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<>(1000);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    queue.offer(new Object());
                    queue.remove();
                }
            }).start();
        }
        while (true) {
            System.out.println("begin scan, i still alive");
            queue.stream()
                    .filter(o -> o == null)
                    .findFirst()
                    .isPresent();
            Thread.sleep(100);
            System.out.println("finish scan, i still alive");
        }
    }
}

程序堵塞
在这里插入图片描述

原因

问题在于LinkedBlockingQueuetryAdvance方法
在这里插入图片描述

stream遍历会调用tryAdvance方法
在这里插入图片描述
tryAdvance只有在下面的情况会出现死循环

  • current != null 为true
  • current.item != null 为false

众所周知LinkedBlockingQueue是链表结构
只有在LinkedBlockingQueue的一个节点自己指向自己的时候才会出现这种死循环的情况。
要把节点从链表上拿掉只可能在移除的时候

再看我们程序还使用了LinkedBlockingQueue的remove方法
在这里插入图片描述
里面调用了LinkedBlockingQueue的poll方法
在这里插入图片描述
poll方法里进行了加锁操作说明是线程安全的,重点看dequeue方法
在这里插入图片描述
自己指向自己来移除节点
dequeue 方法的这个地方和 tryAdvance 方法里面的 while 循环一起出现就会出现死循环的情况

如果调用有参的remove呢?
在这里插入图片描述
在这里插入图片描述

发现处理方式跟无参remove不一样
注释翻译 p.next没有更改,以允许遍历p的迭代器保持其弱一致性保证。
所以带参的 remove 方法是考虑到了迭代器的情况,但是无参的 remove 方法没考虑。

结论

LinkedBlockingQueue 虽然内部有读写锁的存在,一般情况下是线程安全的,但是,在 JDK8 的场景下,当它遇到 stream 操作的时候,又有其他线程在调用无参的 remove 方法,会有一定几率出现死循环的情况。

解决

JDK官方在1.9对此bug进行了修复
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值