并发队列中迭代器弱一致性原理探究

article/2025/4/30 22:20:48

一、前言

并发队列里面的Iterators是弱一致性的,next返回的是队列某一个时间点或者创建迭代器时候的状态的反映。当创建迭代器后,其他线程删除了该元素时候并不会抛出java.util.ConcurrentModificationException异常,能够保持创建迭代器后的元素一定被正确的next出来。

二、 ConcurrentLinkedQueue类图结构

0?wx_fmt=png

以ConcurrentLinkedQueue为例说下是如何实现的,如图内部的Itr类实现了接口Iterator的功能。

三、测试代码

3.1 实验一

本实验是测试获取迭代器后调用next前删除元素看看会有什么结果

0?wx_fmt=png

主线程debug断点查看图:

0?wx_fmt=png

如图主线程获取了队列元素zlx节点的迭代器,在调用next的时候debug阻塞主。

下面激活SleepInterrupt线程,执行remove操作:0?wx_fmt=png

remove后调度到主线程执行0?wx_fmt=png

可知还是迭代出来了已经删除的元素,并且没抛出异常。

3.2 试验2

本实验测试获取迭代器前调用next后删除迭代器后面的元素看看有什么结果0?wx_fmt=png

主线程debug断点图0?wx_fmt=png

下面激活SleepInterrupt线程,执行remove操作:0?wx_fmt=png

remove后调度到主线程执行0?wx_fmt=png

可知现在队列里面没有了gh元素,下面看看迭代结果

3.3 试验3

本实验测试获取迭代器前调用next后删除迭代器后面的元素看看有什么结果0?wx_fmt=png

下面看看迭代结果0?wx_fmt=png

四、源码分析

首先调用队列的iterator()方法时候会实例化一个迭代器,所以每次调用该方法都是一个新的实例,构造函数内部调用了advance方法,目的是确定第一个元素的iterator.

Itr() {advance();//(1)}//获取队列中下一个可用节点。调用next()时候返回节点值,或者返回nullprivate E advance() {    //lastRet记录调用最后一次调用next时候的节点lastRet = nextNode;(2)    //x存放节点值E x = nextItem;(3)    //获取next节点Node<E> pred, p;    //如果为nul则调用阻塞队列的first方法获取if (nextNode == null) {p = first();//(4)pred = null;} else {        //不为nul则获取下一个节点(5)pred = nextNode;p = succ(nextNode);}    for (;;) {        //p=null则直接返回,重置节点null(6)if (p == null) {nextNode = null;nextItem = null;            return x;}        //否者记录当前节点并返回值E item = p.item;        if (item != null) {//(7)nextNode = p;nextItem = item;            return x;} else {            // 跳过null值节点(8)Node<E> next = succ(p);            if (pred != null && next != null)pred.casNext(p, next);p = next;}}
}
//判断是否有原始public boolean hasNext() {    return nextNode != null;
}//有则删除public E next() {    if (nextNode == null) throw new NoSuchElementException();    return advance();
}//删除元素public void remove() {Node<E> l = lastRet;    if (l == null) throw new IllegalStateException();    // rely on a future traversal to relink.l.item = null;lastRet = null;
}

下面看图说话:

0?wx_fmt=png

那么调用队列的iterator时候执行(1)(4)(7)后队列状态图:0?wx_fmt=png

调用hasNext()时候知道nextNode != null所以返回true.

然后调用next()方法执行(2)(5)(7)后,返回zlx,队列状态图0?wx_fmt=png

也就说第一次调用队列的iterator方法会在构造函数调用advance方法一次,这时候已经把队列第一个可用的节点指针赋值给nextNode,节点值赋值给nextItem;这样当调用hasNext时候先看nextNode是否null,null说明队列为空则返回false说明队列里面没有元素,否者会调用next方法,该方法会再次调用advance方法,由于调用hasNext确定了nextNode不为null所以会调用(5)来获取下次调用next要返回的值,也就是当前nextNode的后继节点。如果后继节点为null则返回nextNode对应的值nextItem,否者设置下一次调用next时候需要的nextNode和nextItem。

下面考虑下实验一的情况,首先线程1调用调用hasNext()后情况为:0?wx_fmt=png

0?wx_fmt=png

现在在调用next方法(2)(5)(7)后的状态为:0?wx_fmt=png

试验2的结果很明显,这里不再说了,下面看看试验3

0?wx_fmt=png

然后调用remove方法因为lastRet=null所以抛出了异常,其实应该先调用next方法在调用remove方法。0?wx_fmt=png

然后看remove里面并没有看到有对队列里面的头尾节点进行操作,也就说并没有在队列中移除该元素的操作,乍一看这有问题,但是没问题:下面看删除zlx后队列状态0?wx_fmt=png

也就是remove仅仅把节点内容变为null,所以head还是指向这个元素(注意本节讲的都是不带哨兵节点的队列,正常情况下队列一开始有个null的哨兵节点,如果本节考虑的话,那么上面的图应该有两个null节点,一个是哨兵,一个是zlx节点变成的)

而poll时候如果节点内容为null则会继续查看后继节点,所以这里remove简单的把节点内容变为null即可。

四、总结

并发队列里面的迭代器通过使用nextItem保留创建迭代器时候的节点的值,保证了在调用hasNext和next方法之间其他线程删除该元素后还可以正常返回删除节点的内容,并不抛出异常,之所以说是弱一致性是因为调用next时候该元素已经不在队列里面了,但是迭代返回还可以返回。另外remove操作并没有立刻把删除的原始从队列中干掉,而是在出队时候从队列里面解除,让它变为自引用节点,等待被垃圾回收。

如果感觉写的好,就打赏下小编吧^-^

0?wx_fmt=jpeg


http://chatgpt.dhexx.cn/article/cBxX2Xd6.shtml

相关文章

浅谈同构类问题的骗分算法

ZLX算法-----同构类问题的有力骗分算法前言:ZLX算法是一种解决判定性同构问题的蒙特卡罗式骗分算法:总能在确定的运行时间内出解,但是得到的解不能保证正确。尽管由于具有拓扑序,树同构和仙人掌同构存在多项式算法,但是…

win10下修改C盘用户文件夹名

之前安装一个程序出错,上网百度后是用户文件夹名为中文,也在网上找了好多方法,有同步的,有修改注册表的,最后我找到一个比较简单而且数据保留完整的方法。这种方法也会自动修改用户的环境变量,不过修改完后…

【Windows】Win10-更改c盘下的用户文件夹名

转载ooooohugh的文章,原文地址:https://blog.csdn.net/qq_33530388/article/details/71739845 当初 不小心用自己名字 作为计算机用户名,后来 许多软件因为 不支持 路径中有中文,导致吃了不少的亏,心疼。。。。 下面说…

Windows10更改c盘中用户名对应的文件名字

目录 前言一、修改步骤1.开启管理员账号并登陆2.重启电脑3.登录管理员账号4.重命名用户名对应的文件夹5.修改Path6.重启电脑并切换回你的账号7.修改环境变量8.重启电脑 二、修改导致的问题1.桌面大部分软件快捷方式失效2.部分软件无故消失 三、提醒Last 前言 强烈建议看完此文…

win10 修改c盘用户文件夹名称

c盘用户文件夹如果是中文名 可能会导致需要没必要的麻烦,记录一下修改方法 第一步:如果你电脑不是本地用户administrator,注销当前用户使用administrator登录 按winx 点击关机或注销 在点击注销 注销后如果没有Administrator登录方式&#…

有关windows10修改C盘用户中文名文件夹相关问题的具体解决方案

win10修改用户文件夹名 今天在下载安卓sdk开发工具的时候,安装出现了一个问题如图,左下角提示我们的sdk路径含有非ascll的字符,无法继续安装,其实不只是中文字符,英文字符中间若有空格也不能继续安装。 对于互联网学…

更改计算机用户文件夹,win10系统怎么自定义C盘用户文件夹名称

许多用户在安装win10系统之后,想要让电脑显得更加个性化,就想要给C盘中的用户文件夹名称进行自定义修改,那么win10系统怎么自定义C盘用户文件夹名称呢?接下来给大家分享一下具体的操作步骤。 1、在键盘上按下Windows键X 组合键&am…

Windows 11 的C盘User(用户)文件夹下的用户文件夹名称的修改

背景介绍:由于系统重装导致Windows 11的系统用户名与C盘User(用户)文件夹下的用户名文件夹(公用文件夹旁边的文件夹)出现名称不一致,事例中系统用户名命名为“寂萧”,User(用户&…

win10怎么更改c盘用户计算机名,详解win10系统更改c盘用户名文件夹名称的设置技巧...

电脑一旦开机就会不停的运行,不可避免会出现软硬件问题,win10系统更改c盘用户名文件夹名称就是比较常见的状况,尤其是姑娘们遇到win10系统更改c盘用户名文件夹名称是要难过哭鼻子的,其实小编的经验是碰到win10系统更改c盘用户名文…

Windows 10系统中,如何重命名用户文件夹

免责声明 该方法不适用于所有情况,可能导致数据丢失、计算机无法重启等问题,请提前保护好数据! 背景 许多Windows用户总是喜欢将文件放在用户文件夹(C:\Users\username)下,但有时候会发生一些令人苦恼的…

win10修改C盘用户文件夹下的文件命名

唉!!!以前不太懂这些,取了个中文用户名。结果下载软件的过程中,各种出错,就是路径里面含有中文导致的。 这种情况下还都不能直接重命名,我试了各种方法,都不适合,最后找到…

windows修改用户文件夹名称 更改用户名 修改C盘Users目录下文件夹名称

知乎上的更详细版本 windows修改 C:\Users用户文件夹名称 把中文名修改为英文名 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/509804656 简短的说: 1 - 新建一个管理员账户A(名称随意,不要和其他账户一样),注销…

win11修改C盘用户文件夹名称

新电脑使用microsoft账号进行初始化,导致用户文件夹名称是邮箱前五位,这个真的让人头痛。网上很多方法是直接改名称、改注册表信息等,导致出问题,结果要重装系统,很麻烦。 其实有一个很好的解决方法,如下&a…

如何修改C盘用户文件夹下的用户名

1、WindowsR,然后输入regedit,点击确定。 2、进入注册表编辑器后,依次打开HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ProfileList。 3、打开ProfileList后,打开最后一项,然后在右侧找到Profile…

win10 重命名用户文件夹

背景 win10 修改系统用户名之后发现C:\Users下面的用户文件夹没有跟着改变, 于是手动调整一下 解决办法 操作前,先对重要的数据和配置做一下备份, 修改注册表 按Windows R组合键,打开运行对话框,输入regedit,打开注册表 接着找到路径计算…

window10如何重命名系统用户文件夹

此文章用于帮助重命名系统文件夹 有些软件在使用时会出现保存路径中不能有中文的情况,但又不便修改路径,而系统用户文件夹又是中文无法避开时想要重命名发现难以修改名字。这篇文章将详细介绍如何进行系统用户重命名。 1. 首先进入管理员账户&#xff…

更改C盘用户目录下的用户名(真实有效)

免责声明 以下所有操作均本人实测,目前本人无任何问题(本人win11系统),如果出现任何问题与我无关。可能不同的电脑不同,所以请做好出现任何问题的心理准备,最好提前备份好重要文件! 由于本人电…

关于重命名C盘User文件夹内用户名的心得

非必要千万不要改啊!!!!! 写在前面:本文是对其他同主题博客的补充,仅供参考,只是希望能给大家提供帮助,请勿盲目根据本文所说操作进行,一定要结合实际情况&a…

更改C盘用户目录下的用户名(亲测有效)

免责声明 以下所有操作均来自别人,本人只负责实测,如果出现任何问题与我无关。可能不同的电脑有可能会出现一些问题,所以请做好出现任何问题的心理准备,最好提前备份好重要文件!如果你准备好了,再去进行下…

修改Win10 C盘用户文件夹名称

修改Win10 C盘用户文件夹名称 1.注销当前用户2.登录另一个管理员账号3.修改C盘用户文件夹名4.修改注册表配置5.注销当前用户并登陆以修改的用户6.修改用户账户信息7.重启系统。 首先,你还需要有一个另外一个管理员用户,没有的话创建一个。 1.注销当前用…