为什么volatile在java中适用于ArrayList这样的容器?

为什么volatile在java中适用于ArrayList这样的容器?,java,multithreading,volatile,Java,Multithreading,Volatile,重新分配变量时,volatile起作用。但是当ArrayList发生如下更改时,为什么volatile起作用 package io.github.baijifeilong.thread; import io.vavr.control.Try; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; p

重新分配变量时,
volatile
起作用。但是当
ArrayList
发生如下更改时,为什么
volatile
起作用

package io.github.baijifeilong.thread;

import io.vavr.control.Try;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class VolatileApp3 {

    private static volatile List<Integer> list = new ArrayList<>();


    public static void main(String[] args) {
        new Thread(() -> IntStream.rangeClosed(1, 10).forEach($ -> {
            Try.run(() -> TimeUnit.SECONDS.sleep(1));
            System.out.printf("Appending %d => ", $);
            list.add($);
            System.out.println(list);
        })).start();

        new Thread(() -> {
            while (list.size() < 5) {
//                Try.run(() -> TimeUnit.SECONDS.sleep(0));
            }
            System.out.println("List size >= 5 now");
        }).start();
    }
}

你是说如果另一个线程没有标记为volatile,那么它就看不到
列表的更新吗?很多东西可以在简单的测试中工作,而在实际的并发情况下却不能工作。并发性错误从来都不明显,而且测试总是有可能漏掉它们。这就是我要说的:制作一个
列表
实际上并不能解决任何问题,它仍然是有缺陷的代码。事实上,我也观察到了这一点。据我所知(我可能错了),当系统读取
volatile
变量时,它会更新所有内容的CPU缓存,而不仅仅是变量。这与确保“发生在”规则有关。与Louis Wasserman所说的话相呼应:Java语言保证,如果您遵守规则,您的程序将正常工作。如果你不遵守规则,它不能保证你的程序会被破坏。一个不正确的程序可能在今天可以运行,但在升级操作系统后无法运行,或者它可能在您的计算机上运行,但在其他人的计算机上无法运行,等等。您无法通过测试找到并发错误。(至少,现在还没有。)确保编写正确的多线程代码的唯一方法是理解,并严格遵守规则。你是说如果另一个线程没有标记为
volatile
,那么它就看不到
列表的更新吗?很多事情可以在简单的测试中工作,而在实际的并发情况下却不能工作。并发性错误从来都不明显,而且测试总是有可能漏掉它们。这就是我要说的:制作一个
列表
实际上并不能解决任何问题,它仍然是有缺陷的代码。事实上,我也观察到了这一点。据我所知(我可能错了),当系统读取
volatile
变量时,它会更新所有内容的CPU缓存,而不仅仅是变量。这与确保“发生在”规则有关。与Louis Wasserman所说的话相呼应:Java语言保证,如果您遵守规则,您的程序将正常工作。如果你不遵守规则,它不能保证你的程序会被破坏。一个不正确的程序可能在今天可以运行,但在升级操作系统后无法运行,或者它可能在您的计算机上运行,但在其他人的计算机上无法运行,等等。您无法通过测试找到并发错误。(至少,现在还没有。)确保编写正确的多线程代码的唯一方法是理解并严格遵守规则。
Appending 1 => [1]
Appending 2 => [1, 2]
Appending 3 => [1, 2, 3]
Appending 4 => [1, 2, 3, 4]
Appending 5 => [1, 2, 3, 4, 5]
List size >= 5 now
Appending 6 => [1, 2, 3, 4, 5, 6]
Appending 7 => [1, 2, 3, 4, 5, 6, 7]
Appending 8 => [1, 2, 3, 4, 5, 6, 7, 8]
Appending 9 => [1, 2, 3, 4, 5, 6, 7, 8, 9]
Appending 10 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]