11

知道

对于声明为 volatile 的所有变量,读写都是原子的

问题1:这是否可以理解为

private volatile int x = 0;

x++;操作是原子的?

然后

将变量标记为 volatile 并不能消除同步原子操作的所有需要​​,因为仍然可能出现内存一致性错误。

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

换句话说,是否应该标记所有需要防止并发修改的变量volatile

4

3 回答 3

13

volatile 只为您提供额外的可见性保证、longs/doubles 的原子写入/读取(否则 JLS 不保证,是的)和一些内存顺序保证。没有同步(虽然可以从 volatile 开始构建同步块 - Dekker 的算法)所以不,它对您没有帮助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 它可能不会。

于 2012-07-08T19:06:13.503 回答
7

x++; 操作是原子的?

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

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

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

于 2012-07-08T19:05:22.023 回答
0

作为可以说明先前答案的快速可测试示例,这始终产生最终计数 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();
        }
    }
}


}
于 2013-12-06T12:16:00.883 回答