关于 volatile 的一些小问题

2021-03-01  本文已影响0人  想54256

title: 关于 volatile 的一些小问题
date: 2021/03/01 13:46


  1. ArrayList 为啥线程不安全

在 add 方法中的 elementData[size++] = e 存在线程不安全的风险。

elementData 与 size 都是全局变量,但没有进行同步处理,elementData是共享的线程不安全的可变数据。

  1. 使用 volatile 修饰 List 是线程安全吗?

很明显,如果你有这个疑问那么肯定你对上面的那个问题还不了解,ArrayList 具有线程安全问题的原因是 add 方法对 size 和 elementData 的操作并不具有原子性。

而使用 volatile 修饰 List 只是让其他线程对这个变量具有了可见性,对他的操作还是不具有原子性,所以还是线程不安全的。

  1. volatile 与 AtomicReference

volatile 修饰的变量具有可见性、有序性,但是不具有原子性,多个线程对变量的操作可能会出现问题。

AtomicReference 保证了包装的对象的引用修改时的原子性。

  1. 为什么 AtomicReferenceFieldUpdater(字段更新器)要配合 volatile 使用

因为修改之后其他线程要立刻可见啊,AtomicReferenceFieldUpdater 只是原子更新,防止更新的时候失败。

附录:volatile

定义

volatile 是 Java 提供的一种稍弱的同步机制,用于确保将变量的更新操作通知到其他线程(虽然我不知道他是怎么通知的)。当把变量声明为 volatile 类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。修改 volatile 变量时,会将数据立即写回内存,然后导致缓存了该数据的其他cpu的缓存无效,这样其他线程在使用该数据的时候必须从内存重新读取数据到缓存,保证了可见性。

注意:将可变对象字段标记为 volatile 意味着对象引用是 volatile,但是对象本身不是,其他线程可能看不到对象状态的更新。举个例子吧:

volatile List<User> userList = new ArrayList<>();

public static void main(String[] args) {

    User u = new User("zs");

    // 修改了 ArrayList 内部的 elementData 属性,不会通知其他线程(虽然我不知道他是怎么通知的)
    userList.add(u);

    // 修改了 ArrayList 内部的 elementData 的第一个元素的 name 属性,不会通知其他线程(虽然我不知道他是怎么通知的)
    u.setName = "ls";

    // 修改了 ArrayList 内部的 elementData 的第一个元素的引用,不会通知其他线程(虽然我不知道他是怎么通知的)
    userList.add(0, new User("ww"));

    // 修改了 userList 的引用,会通知其他线程
    userList = new ArrayList<>();


    // 也就是说虽然 userList 使用了 volatile 修饰,但也只是保证了他自己的可见性,至于其中的 elementData 属性还有 elementData 属性中的 User 对象都是不保证可见性的,其他线程如果执行 u.setAge
}

// 注:以上只是理论上的逻辑,使用代码无法测试出来,好像是因为不同版本不同厂商的虚拟机,执行策略都不一样 0.0.. [volatile修饰数组或引用对象的问题](https://www.jianshu.com/p/10ef84dc5eef)

结论

volatile 只保证修饰的变量的可见性,对其内容不保证可见性。

上一篇下一篇

猜你喜欢

热点阅读