Snailclimb / Snailclimb/JavaGuide

关于Java 并发常见面试题总结(中)“如何保证变量的可见性?”问题的答案详解

オープン
#1,849 コメント 2 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

enhancement
主要言語
JavaScript
スター
159k
フォーク
46.1k
平均マージ
14時間 36分
マージ済み PR(30日)
6

説明

Guide哥你好,问题见下图,参考众多权威文献以及文章我认为,Java volatile关键字保证可见性是基于总线嗅探+缓存一致性协议(MESI协议),所以我认为并不是每次使用都会到主存中获取。

而MESI协议具体,我们可以从四个字母分别代表4种状态来理解:

  1. Modify(修改):当缓存行中的数据被修改时,该缓存行置为M状态
  2. Exclusive(独占):当只有一个缓存行使用某个数据时,置为E状态
  3. Shared(共享):当其他CPU中也读取某数据到缓存行时,所有持有该数据的缓存行置为S状态
  4. Invalid(无效):当某个缓存行数据修改时,其他持有该数据的缓存行置为I状态

image

以下图为例,我认为Java volatile变量保证可见性的原因,可以从这样一个场景理解(假设线程1在CPU1上,线程2在CPU上)
CPU1读取数据a=1,CPU1的缓存中都有数据a的副本,该缓存行置为(E)独占状态
CPU2也执行读取操作,同样CPU2也有数据a=1的副本,此时总线嗅探到CPU1也有该数据,则CPU1、CPU2两个缓存行都置为(S)共享状态
CPU1修改数据a=2,CPU1的缓存以及主内存a=2,同时CPU1的缓存行置为(S)状态,总线发出通知,CPU2收到通知将缓存行置为(I)无效状态
CPU2再次读取a,虽然CPU2在缓存中命中数据a=1,但是发现状态为(I),因此直接丢弃该数据,去主内存获取最新数据

image

所以我认为应该改为(答案已提交PR,劳烦Guide哥看看是否得当):
在 Java 中,volatile 关键字底层是基于总线嗅探机制和MESI缓存一致性协议保证变量的可见性,如果我们将变量声明为 volatile ,这就指示CPU,这个变量是共享且不稳定的,每次使用它时,处理器都会判断这个变量在缓存行中的状态是否因为其他处理器的修改变成无效(Invalid),若无效则重新从系统内存中把数据读取到处理器缓存。

参考文章:

  1. https://zhuanlan.zhihu.com/p/250657181
  2. Java书籍《Java并发编程的艺术》

image

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

「如何保证变量的可见性?」というタイトルのJava concurrency面接回答から始め、このissueで提案されている説明と引用元を確認してください。volatile、キャッシュコヒーレンシ、メインメモリについての記述を検証し、回答が修正され、提出されたPRまたはmaintainerの判断が解決した時点で作業完了とみなしてください。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
java
領域
documentation
issue の種類
ドキュメント
難易度
3/5
見積もり時間
1〜2日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。