ConcurrentHashMap 为啥在initTable方法中调用thread.yield()为啥不使用Thread.sleep(0)

最近学习ConcurrentHashMap源码中发现其put时,如果当前table为空则会调用 initTable() 进行初始化。

跟踪initTable 方法发现:

initTable()方法负责初始化内部哈希表的数组。这个方法是线程安全的,它使用了一种乐观锁机制来确保只有一个线程能够初始化哈希表。让我们逐步分析这个方法的工作流程:

  1. 首先,方法通过一个while循环检查当前的table是否为null或长度为0,这表示哈希表尚未初始化。

  2. 如果table是未初始化的,它会检查sizeCtl变量的值。这个变量用于控制表的初始化和扩容操作。如果sizeCtl的值小于0,这意味着另一个线程已经在初始化或者正在扩容哈希表。

  3. 当检测到sizeCtl小于0时,当前线程调用Thread.yield()。这是因为当前线程"输掉了初始化竞赛"(lost initialization race),即它发现有另一个线程已经在进行初始化操作。在这种情况下,当前线程通过yield()让出CPU,希望给正在进行初始化的线程一个执行的机会,从而减少无谓的CPU竞争。

  4. 如果sizeCtl大于等于0,当前线程尝试使用compareAndSwapInt(CAS操作)来设置sizeCtl的值为-1,这是为了表示当前线程将负责初始化表。如果CAS操作成功,当前线程进入try块进行初始化。

  5. try块中,再次检查table是否未初始化,以防在CAS操作和这次检查之间有其他线程完成了初始化。如果table仍然未初始化,它将创建一个新的数组,并将sizeCtl设置为一个新的阈值,这个阈值是根据默认容量或者给定的初始容量计算出来的。

  6. 最后,finally块中sizeCtl被设置为新的阈值,表示初始化完成,其他线程可以开始使用哈希表了。

为啥使用Thread.yield(); 因为在JDK 1.8中,ConcurrentHashMapinitTable()方法是用于初始化内部哈希表的。在这个方法中,如果检测到有其他线程也正在尝试初始化哈希表(通过检查sizeCtl字段),当前线程会调用Thread.yield()。这是一种启发式的方法,目的是为了减少资源的浪费和竞争,让出CPU时间片给其他可能正在运行的线程,以便它们能够完成初始化工作。

Thread.yield()是一种提示调度器当前线程愿意放弃其当前的时间片,但是它并不保证会导致当前线程立即停止执行。调度器可能会忽略这个提示。在多数情况下,yield()的作用是让相同优先级的其他线程有机会执行,但是它不会导致锁等资源的释放,也不会导致线程状态的改变。

Thread.yield()相比,Thread.sleep(0)会导致当前线程暂停执行指定的时间(在这个例子中是0毫秒),并进入TIMED_WAITING状态。即使是0毫秒,sleep(0)也会请求调度器重新进行一次调度,但是它不会像yield()一样仅仅是一个提示,而是确实会导致当前线程暂停。在sleep(0)期间,其他线程可以利用CPU执行任务。

总的来说,Thread.yield()是一种对调度器的非强制性提示,让出CPU以便其他线程可以运行,但是不保证会有任何效果。而Thread.sleep(0)实际上会导致线程暂停执行,即使时间非常短,也会给其他线程执行的机会。

ConcurrentHashMapinitTable()方法中,使用Thread.yield()是为了在发现有其他线程正在进行初始化时,让当前线程暂时让出CPU,从而减少不必要的资源竞争,这是一种尝试使得初始化工作更高效的策略。

通过使用Thread.yield()ConcurrentHashMap试图减少无谓的自旋等待,这在多线程环境中可能会导致较高的CPU消耗。如果不使用yield(),那么在初始化哈希表时,其他线程将会在while循环中忙等待,这将导致CPU资源的浪费,尤其是在高负载的服务器环境中。

总之,Thread.yield()在这里是作为一种策略,用来提高多线程环境下的CPU使用效率,减少因自旋等待导致的资源浪费。

相关推荐
SQL-First布道者6 分钟前
全面解构传统持久层框架,拥抱真正的 SQL-First
java·数据库·spring boot·sql·spring·mybatis·spring jdbc
小的~~30 分钟前
面试被问分布式锁,我差点语塞…直到搞懂了Redis、ZooKeeper和etcd的“三国杀”
redis·分布式·面试
小蒜学长39 分钟前
vue旅游攻略网站(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端·旅游
凤山老林1 小时前
轻量级规则引擎落地:Spring Boot 集成 LiteFlow 实现动态业务编排与热更新
java·spring boot·后端·规则引擎·liteflow
IT_陈寒1 小时前
Python装饰器把我坑惨了,原来这样用才不掉链子
前端·人工智能·后端
夏贰四1 小时前
管控数据加载调度如何规避任务冲突?数据加载运维监控体系该如何搭建?
java·大数据·运维
山甫aa1 小时前
JavaWeb后端开发学习手册
java·开发语言·数据库·学习·mysql·springboot·web
程序员爱钓鱼1 小时前
Rust Associated Type关联类型详解:为Trait定义内部类型
前端·后端·rust
程序员爱钓鱼1 小时前
Go 编程实战:匿名函数 Anonymous Function——没有名字的函数与灵活回调
后端·面试·go
鱼鳞_1 小时前
热门八股-JVM
java·jvm