【问题标题】:Can volatile make common variables visible for different threads?volatile可以使不同线程的公共变量可见吗?
【发布时间】:2020-09-06 05:52:49
【问题描述】:
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 只能使自己(不是之前的常见变量)对其他线程可见。所以我想对其进行测试,但没有打印任何内容。

【问题讨论】:

  • 你的主线程还在运行吗?还是说你的整个程序已经结束了?
  • 我的主线程可以完成,但关键是'volatile'。答案已经发布在下面的 cmets 部分。

标签: java locking volatile barrier


【解决方案1】:

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

访问link 了解更多信息。

【讨论】:

    【解决方案2】:

    Java 内存模型被定义为一组先发生的规则。使用 a 发生在规则之前,您可以保证如果 'a 发生在 b 之前',而不是 'b 将看到 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之间存在先发生关系。这称为程序顺序规则。

    Action 2 和Action 3 之间有一个发生在Action 3 之前的关系,因为Action2 发生在Action 3 之前。这称为volatile 变量规则。

    由于Action 3由于程序顺序在Action 4之前发生,Action 3和Action 4之间存在先发生关系。这又是程序顺序规则。

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

    所以线程 2 将看到 a 的正确值。

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

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

    【讨论】:

      【解决方案3】:

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

      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();
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 1970-01-01
        • 2013-08-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多