V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  Java

为什么 AtomicIntegerArray 的 array 成员不是 volatile 而是 final 的啊?

  •  
  •   amiwrong123 · 2020-05-10 18:57:29 +08:00 · 2123 次点击
    这是一个创建于 1651 天前的主题,其中的信息可能已经有所发展或是发生改变。

    对比两个原子类:

    public class AtomicIntegerArray implements java.io.Serializable {
    
        private final int[] array;
        
        public final int get(int i) {
            return getRaw(checkedByteOffset(i));
        }
    
        private int getRaw(long offset) {
            return unsafe.getIntVolatile(array, offset);
        }
    
    public class AtomicStampedReference<V> {
        private static class Pair<T> {
            final T reference;
            final int stamp;
            private Pair(T reference, int stamp) {
                this.reference = reference;
                this.stamp = stamp;
            }
            static <T> Pair<T> of(T reference, int stamp) {
                return new Pair<T>(reference, stamp);
            }
        }
    
        private volatile Pair<V> pair;
        
        public V getReference() {
            return pair.reference;
        }
    
        public int getStamp() {
            return pair.stamp;
        }
    

    第一个问题就是:为什么 AtomicIntegerArray 的 array 成员不是 volatile 而是 final 的啊?为啥这么设计啊

    第二个问题就是:为什么 AtomicIntegerArray 的 get 函数不能像 AtomicStampedReference 一样实现呢?是因为成员不是 volatile 的呗。直接像下面这样实现,不行吗。如果改成 private volatile int[] array;,是不是就可以了?

        public final int get(int i) {
            return array[i];  //类似 pair.reference;
        }
    

    各位大佬,求解答啊

    7 条回复    2020-05-11 22:36:56 +08:00
    hoholiday
        1
    hoholiday  
       2020-05-10 19:10:58 +08:00 via Android
    了解一下 unsafe 类
    amiwrong123
        3
    amiwrong123  
    OP
       2020-05-10 19:44:23 +08:00
    @hoholiday
    了解了啊。要知道大部分原子类,其数据成员都是 volatile 的,其 get 方法都是直接 return 成员,而不是通过 unsafe 对象 get 。
    但原子数组类,在设计上就不一样了。
    iffi
        4
    iffi  
       2020-05-10 20:05:50 +08:00
    AtomicIntegerArray 的 set 方法,通过 putIntVolatile 方式修改(修改的是 array 的某个 index 对应的值,不用对整个 array 加 volatile 修饰)
    public final void set(int i, int newValue) {
    unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }

    AtomicStampedReference 的 set 方法,直接=赋值修改,并发情况下,为了内存可见性采用 volatile 修饰
    public void set(V newReference, int newStamp) {
    Pair<V> current = pair;
    if (newReference != current.reference || newStamp != current.stamp)
    this.pair = Pair.of(newReference, newStamp);
    }
    amiwrong123
        5
    amiwrong123  
    OP
       2020-05-10 20:20:08 +08:00
    @iffi
    那如果修改 AtomicIntegerArray 为 private volatile int[] array,再修改 set 函数为 public final void set(int i, int newValue) {array[i] = newValue; } 。

    这样是否可以保证 可见性 呢
    iffi
        6
    iffi  
       2020-05-10 20:38:02 +08:00
    @amiwrong123 这个 array 创建好了之后,不会修改其引用指向,所以 private volatile int[] array 不符合语义。修改 set 函数为 public final void set(int i, int newValue) {array[i] = newValue; } 我猜不会保证可见性(你也可以复制 JDK 的源码修改并自己实验验证一下),除非对 array 每一个元素都加 volatile 修饰。这也是为什么要用 unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    SoloCompany
        7
    SoloCompany  
       2020-05-11 22:36:56 +08:00
    你需要的是 AtomicReference 而不是 AtomicXxxArray
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1908 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:28 · PVG 00:28 · LAX 08:28 · JFK 11:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.