1、volatile は変数値の可視性を保証します
この点については、Java で変数値とは何かを理解する必要があります。基本型の変数にとって、変数値とはそれに与えられた値のことです。例えば、int a=1 とすると、1 が基本型変数 a の変数値です。一方、参照型の変数にとって、変数値とはヒープ内のアドレスです。例えば、Object a=new Object () とすると、0x11111111 が変数 a の変数値です。
以上から、基本型の変数に対しては、新しい値を与えることで変数値を変更します。例えば、a=2 とすると、変数値が変更されます。一方、参照型の変数に対しては、アドレスを変更することで変数値を変更します。例えば、a=new Object () とすると、変数値が変更されます。
2、配列型は内部要素の可視性を保証します
volatile int [] a = new int [4] とすると、実際には配列の各要素 a [0]、a [1]、a [2]、a [3] はすべて可視性を持ちます。可視性の判断については、第 1 条を参照してください(配列の各要素は変数と見なすことができます)。
3、volatile 変数への代入は新しい変数の可視性を保証しません
volatile Object a = new Object();
Object b = a;
ここで volatile は変数 b の可視性を保証しません。
concurrentHashMap と CopyOnWriteArrayList では異なることがわかります:
CopyOnWriteArrayList:
第 1 の図では、a は実際には array そのものであり、したがって可視性を持ち、a [index] を直接使用して値を安全に取得できます。
concurrentHashMap:
第 2 の図からわかるように、table は新しい変数 tab に代入されています。table 自体は volatile 修飾されていますが、新しい変数 tab は可視性を持たないため、値を安全に取得するには Unsafe クラスの getObjectVolatile を使用する必要があります。