【问题标题】:Uses of volatile without synchronization没有同步的 volatile 的使用
【发布时间】:2012-07-08 18:55:08
【问题描述】:

知道

对于所有声明为 volatile 的变量,读取和写入都是 atomic

问题1:可以这样理解吗

private volatile int x = 0;

x++; 操作是原子的?

还有那个

标记变量 volatile 并不能消除所有同步的需要 原子动作,因为memory consistency errors are still possible.

问题 2: 我想知道在什么情况下(如果有)可以看到标记为 volatile 的变量,而看不到标记为同步的块的任何方法(尝试访问/修改变量)?

换句话说,是否所有需要保护以防止并发修改的变量都标记为volatile

【问题讨论】:

    标签: java volatile


    【解决方案1】:

    volatile 只为您提供额外的可见性保证、longs/doubles 的原子写入/读取(否则 JLS 不保证,是的)和一些内存顺序保证。 同步(虽然可以构建以 volatile 开头的同步块 - Dekker's algorithm ) 所以不,x++ 对您没有帮助 - 这仍然是读取、inc 和写入,需要某种形式的同步。

    volatile 的一个例子是著名的双重检查锁定,我们在大多数情况下避免同步,因为我们只需要顺序保证:

    private volatile Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
    

    一个绝对不涉及同步的例子是一个简单的退出标志,这里它不是关于排序保证,而只是关于保证可见性

    public volatile boolean exit = false;
    public void run() {
       while (!exit) doStuff();
       // exit when exit set to true
    }
    

    如果另一个线程设置exit = true,则执行while循环的另一个线程可以保证看到更新 - 如果没有 volatile 它可能不会。

    【讨论】:

    • 优秀的答案和例子。谢谢。
    【解决方案2】:

    x++;操作是原子的?

    没有。这减少到x = x + 1x 的读取是原子的,x 的写入是原子的,但 x = x + 1 整体上不是原子的。

    我想知道在什么情况下(如果有)可以看到标记为 volatile 的变量,而看不到标记为同步的任何块方法(尝试访问/修改变量)?

    嗯,有各种不使用synchronized 的并发方法。 Java 中有各种各样的其他锁定实用程序,仍然需要 volatile:ConcurrentLinkedQueue 之类的无锁算法就是一个具体的例子,尽管它广泛使用了“神奇的”compareAndSet 原子。

    【讨论】:

      【解决方案3】:

      作为一个可快速测试的示例,可以说明先前的答案,这始终产生最终计数 8:

      import java.util.concurrent.atomic.AtomicInteger;
      
      
      public class ThreadTest_synchronize {
      
      public static void main(String[] args) {
      
          ThreadTest_synchronize tt = new ThreadTest_synchronize ();
          try {
              tt.go();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      
      }
      
      private void go() throws InterruptedException{
      
          MyRunnable t = new MyRunnable();
          Thread myThread_1 = new Thread( t, "t1");
          Thread myThread_2 = new Thread( t, "t2");
          myThread_1.start();
          myThread_2.start();
          myThread_1.join();
          myThread_2.join();
          System.out.println("Processing count="+t.getCount());       
      
      }
      
      private class MyRunnable implements Runnable{
      
          private AtomicInteger count=new AtomicInteger(0);
      
          @Override
          public  void run() {
              for(int i=1; i< 5; i++){
                  doSomething(i);
                  count.getAndAdd(1);
              }           
          }
      
      
          public AtomicInteger getCount() {
              return this.count;
          }
      
      
          private void doSomething(int i) {
              try {
                  Thread.sleep(i*300);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }       
      
      }
      

      虽然这通常不会:

      public class ThreadTest_volatile {
      
      public static void main(String[] args) {
      
          ThreadTest_volatile tt = new ThreadTest_volatile ();
          try {
              tt.go();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      
      }
      
      private void go() throws InterruptedException{
      
          MyRunnable t = new MyRunnable();
          Thread myThread_1 = new Thread( t, "t1");
          Thread myThread_2 = new Thread( t, "t2");
          myThread_1.start();
          myThread_2.start();
          myThread_1.join();
          myThread_2.join();
          System.out.println("Processing count="+t.getCount());       
      
      }
      
      private class MyRunnable implements Runnable{
      
          private volatile int count = 0;
      
      
          @Override
          public  void run() {
              for(int i=1; i< 5; i++){
                  doSomething(i);
                  count++;
              }
      
          }
      
          private  int add(int count){
              return ++count;
          }
      
      
          public int getCount(){
              return count;
          }
      
          private void doSomething(int i) {
      
              try {
                  Thread.sleep(i*300);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-25
        • 2017-03-13
        • 1970-01-01
        • 2011-08-22
        • 2014-05-16
        • 2019-05-06
        • 1970-01-01
        相关资源
        最近更新 更多