【问题标题】:Visibility issues when writing to array concurrently同时写入数组时的可见性问题
【发布时间】:2016-09-23 10:46:49
【问题描述】:

在 Java 中,如果多个线程异步写入原始(双)数组的不同部分,并且(仅)主线程在所有线程完成写入后从数组中读取。

  1. 是否存在可见性问题,即有可能读取或写入原始数组的线程本地版本?
  2. 如果 1. 为真,是否可以通过在读取主线程之​​前添加内存屏障来解决?您会建议使用哪一种?

下面的示例代码。

提前非常感谢, 曼努埃尔

    //Example code to calculate distance of 1 point to 1mln other points:
    double[][] history = this.createRandomMatrix(1000000,8);
    double[] order = this.createRandomMatrix(1,8)[0];
    double[] result = new double[1000000];
    for(int i = 0; i< 100;i++){
        pool.execute(new Calculator(history, newPoint, result,i*10000,10000    + i * 10000));
    }
    pool.shutdown();
    try {
        pool.awaitTermination(1, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //read happens here

    //Calculator run function
    public void run() {
        for(int i = start;i<end;i++){
            result[i] = this.computeDistance(history[i],order);
        }
    }

【问题讨论】:

    标签: java concurrency visibility


    【解决方案1】:

    由于它是一个数组(静态数组),除非intentionally created,否则不会创建线程本地版本。每个块都将读取/写入同一内​​存块(数组的内存块)中的地址。由于写入是对数组的不同部分进行的,因此不会出现data races。由于您说在其他线程完成写入后由主线程完成读取,因此不需要同步。

    【讨论】:

    • 谢谢,我能问一下为什么不会创建线程本地版本(结果在我的情况下不会是静态的 - 它只是一个代码 sn-p)。我同意不会发生数据竞争,但是为什么您认为主线程可以保证能够读取工作线程的所有写入 - 即使它发生在之后?我通过运行一些测试来观察这一点 - 只是不确定是否/为什么可以保证?
    • 如果为您启动的所有工作线程创建了线程本地版本的变量,那么使用线程将没有意义,或者您必须将工作线程的结果存储在主线程的变量中,否则他们会迷路的。当您有一些不是线程安全的对象时使用线程局部变量,但您希望避免同步访问该对象。在您的问题中,您说“一旦所有线程完成写入,主线程就会从数组中读取。”。我从中得到的是你暂停主线程直到工作线程完成。
    • 道歉-我认为我对“本地线程”的使用具有误导性-我的意思是各个线程可以缓存结果数组或其字段,并且在主线程从数组中读取之前不会刷新。我不是在谈论 ThreadLocal 的针对性使用。
    【解决方案2】:

    存在可见性问题。这并不意味着每个线程都有它的本地副本。这只是意味着不能保证其他线程写入的值对主线程可见。

    与其尝试设置内存屏障(我想不出任何想法来同步对result 数组的所有访问,这可能会破坏多线程的全部目的),我建议将Callables 提交给您的执行程序:

    List<Future> futures = ExecutorService#invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>)
    

    然后每个线程都可以返回自己的结果,您的主线程将使用它们并获得最终结果。不需要共享状态result。

    【讨论】:

    • 多年来,我一直在做 OP 在 32 位和 64 位机器上所做的事情,并且从未遇到过可见性问题。现在,某处可能有一台机器使它成为问题,所以我不会说它永远不会发生。我只是不明白为什么 OP 正在等待线程完成。
    • 谢谢,听起来很有道理——你能解释一下下面的说法吗? > 存在可见性问题。这并不意味着每个线程都有它的本地副本。这只是意味着不能保证其他线程写入的值将对主线程可见。是否存在我们缺少其他线程的写入但与线程本地缓存无关的情况?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多