volatile 能否使公共变量对不同线程可见?

Can volatile make common variables visible for different threads?

public class TestMemVisbility {
    static  volatile int flag;
    static int[] arr=new int[100000];
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(flag==0) ;
                for(int i=0;i<100000;i++)
                    if(arr[i]!=i) System.out.println("false");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                flag=0;
                for(int i=0;i<100000;i++)
                    arr[i]=i;
                flag=1;
            }

        }).start();
    }
}

据我所知,volatile 只能让其他人看到它自己(不是它之前的普通变量)threads.So我想测试它,但那里什么也没有打印。

我认为您的代码存在一些问题

public class TestMemVisbility {
static  volatile int flag;
static int[] arr=new int[100000];
public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            while(flag==0) ;  // this may hold the CPU in forever and never give the second thread a chance to run.
            for(int i=0;i<100000;i++)
                if(arr[i]!=i) System.out.println("false");
        }
    }).start();
    new Thread(new Runnable() {
        @Override
        public void run() { //could nerver be able to run, or maybe have too wait thread 1 to finish, before its turn
            flag=0;
            for(int i=0;i<100000;i++)
                arr[i]=i;
            flag=1;
        }

    }).start();

    //your main thread is finished too early for the other two threads to run.
}

你想看看volatile是如何工作的,你可以对代码做一些调整:

new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++) {
                    System.out.println("1");
                    if (flag == 1 && arr[i] == i) { 
                        System.out.println("false"); //here you know flag is changed
                    }
                    try {
                        Thread.sleep(20); //release the CPU resources if it running first
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                flag=0;
                for(int i=0;i<100;i++) {
                    arr[i] = i;
                    System.out.println("2");
                }
                flag=1;
            }

        }).start();

        try {
            Thread.sleep(1000);  // main thread hold til give other two threads time to finish
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

Java 内存模型定义为一组发生在规则之前。使用发生在规则之前的规则,您可以保证如果 'a happens before b' 比 'b will see the effects of a'.

想象一下我们会有以下代码:

int a;
volatile int b;

void foo(){
   a=1; //Action1
   b=1; //Action2
}

void bar(){
   int _b = b; //Action3
   int _a = a; //Action4
   ...
}

一个线程调用 foo,它完成后,另一个线程调用 bar。第二个线程会看到 a 的正确值吗?

由于程序顺序,Action1先于Action2发生,因此Action1和Action 2之间存在happens before关系,称为程序顺序规则。

Action 2 和 Action 3 之间存在 happens before 关系,因为 Action2 发生在 Action 3 之前。这称为可变变量规则。

由于程序顺序导致Action 3先于Action 4发生,因此Action 3和Action 4之间存在happens before关系,这又是程序顺序规则。

Java内存模型的另一个重要规则是传递性规则,因此如果 A 发生在 B 之前,B 发生在 C 之前,那么 A 发生在 C 之前。我们可以在这里应用此规则两次并确定Action 1 和 Action 4 之间存在 happens before 关系。

因此线程 2 将看到 a 的正确值。

volatile 可以使 'non volatile' 变量对另一个线程可见。一个非常实际的例子是一些 POJO 被 1 个线程排入 BlockingQueue 队列并被另一个线程出队。这个 POJO 可以是没有任何可变字段的常规对象。队列会根据监视器锁规则或易变变量规则提供happens before 关系。在文档中,这通常称为内存一致性效应。

注意。 volatile 变量规则只能应用于同一对象实例的 volatile 字段。

从 JDK1.5 开始,“volatile 只能使它自己(不是它之前的公共变量)对其他线程可见” 不再正确。

访问此 link 了解更多信息。