为了账号安全,请及时绑定邮箱和手机立即绑定

Java数组元素和内存可见性问题

Java数组元素和内存可见性问题

慕村9548890 2023-08-09 17:30:13
我已经阅读了一些关于来自多个线程的 Java 数组元素可见性的问题和答案,但我仍然无法真正理解某些情况。为了演示我遇到的问题,我提出了一个简单的场景:假设我有一个简单的集合,通过将元素散列到一个存储桶中,将元素添加到其中一个存储桶中(存储桶就像某种列表) 。并且每个桶都是单独同步的。例如:private final Object[] locks = new Object[10];private final Bucket[] buckets = new Bucket[10];这里有一个水桶i应该由 来守护lock[i]。添加元素代码如下所示:public void add(Object element) {        int bucketNum = calculateBucket(element); //hashes element into a bucket        synchronized (locks[bucketNum]) {            buckets[bucketNum].add(element);        }    }由于“桶”是最终的,因此即使没有同步,也不会有任何可见性问题。我的猜测是,通过同步,如果没有最终结果,也不会有任何可见性问题,这是正确的吗?最后,有点棘手的部分。假设我想从任意线程复制并合并所有存储桶的内容并清空整个数据结构,如下所示:public List<Bucket> clear() {    List<Bucket> allBuckets = new List<>();    for(int bucketNum = 0; bucketNum < buckets.length; bucketNum++) {        synchronized (locks[bucketNum]) {            allBuckets.add(buckets[bucketNum]);            buckets[bucketNum] = new Bucket();        }        }    return allBuckets;}我基本上用新创建的存储桶交换旧存储桶,然后返回旧存储桶。这种情况与前一种情况不同add(),因为我们没有修改数组中引用引用的对象,而是直接更改数组/引用。请注意,当我持有存储桶 1 的锁时,我不关心存储桶 2 是否被修改,我不需要结构完全同步和一致,只需可见性和接近一致性就足够了。因此,假设 everybucket[i]只在 下修改过lock[i],你会说这段代码有效吗?我希望能够了解原因和原因,并更好地掌握可见性,谢谢。
查看完整描述

2 回答

?
慕后森

TA贡献1802条经验 获得超5个赞

第一个问题。

locks在这种情况下,线程安全取决于对包含and buckets(我们称之为)的对象的引用是否Container被正确共享。

试想一下:一个线程正忙于实例化一个新Container对象(分配内存、实例化数组等),而另一个线程开始使用这个半实例化的对象,其中locksbuckets仍然为 null(它们还没有被第一个线程实例化)。在这种情况下,这段代码:

    synchronized (locks[bucketNum]) {

变得破碎并抛出NullPointerException。关键字final可以防止这种情况发生,并保证当对的引用不为空时Container,其最终字段已被初始化:

当对象的构造函数完成时,该对象被认为已完全初始化。仅在对象完全初始化后才能看到对该对象的引用的线程保证能看到该对象的最终字段的正确初始化值。( JLS 17.5 )

第二个问题。

假设locksbuckets字段是最终的,并且您不关心整个数组的一致性,并且“每个存储桶 [i] 只在 lock[i] 下修改”,则此代码很好。


查看完整回答
反对 回复 2023-08-09
?
森林海

TA贡献2011条经验 获得超2个赞

在你问的第一个问题中

由于“桶”是最终的,因此即使没有同步,也不会有任何可见性问题。我的猜测是,通过同步,如果没有最终结果,也不会有任何可见性问题,这是正确的吗?

我不确定你所说的“可见性问题”是什么意思,但可以肯定的是,synchronized如果多个线程访问buckets[i]其中一个线程并修改它(例如写入它),那么如果没有这段代码,这段代码将是不正确的。无法保证一个线程所写入的内容对另一线程可见。这还涉及存储桶的内部结构,可能会通过调用来修改add

请记住,finalonbuckets仅涉及对数组本身的单个引用,而不涉及其单元格。


查看完整回答
反对 回复 2023-08-09
  • 2 回答
  • 0 关注
  • 118 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信