volatile和synchronized之间有什么区别?

时间:2016-03-15 21:52:59

标签: java multithreading synchronized volatile java-memory-model

我试图了解volatile在这里是如何运作的。如果我将cc声明为volatile,我会得到以下输出。我知道线程执行输出会不时变化,但我读到volatilesynchronized相同的地方,为什么我得到这个输出?如果我使用Thread1的两个实例那么重要吗?

    2Thread-0
    2Thread-1
    4Thread-1
    3Thread-0
    5Thread-1
    6Thread-0
    7Thread-1
    8Thread-0
    9Thread-1
    10Thread-0
    11Thread-1
    12Thread-0
public class Volexample {
    int cc=0;

    public static void main(String[] args) {
        Volexample ve=new Volexample();
        CountClass count =ve.new  CountClass();
        Thread1 t1=ve.new Thread1(count);
        Thread2 t2=ve.new Thread2(count);
        t1.start();
        t2.start();
    }

    class Thread1 extends Thread{
        CountClass count =new  CountClass();

        Thread1(CountClass count ){
            this.count=count;
        }

        @Override
        public void run() {
            /*for(int i=0;i<=5;i++)
            count.countUp();*/
            for(int i=0;i<=5;i++){
                cc++;
                System.out.println(cc + Thread.currentThread().getName());
            }
        }
    }

    class Thread2 extends Thread {
        CountClass count =new  CountClass();

        Thread2(CountClass count ){
            this.count=count;
        }

        @Override
        public void run(){
            /*for(int i=0;i<=5;i++)
            count.countUp();*/
            for(int i=0;i<=5;i++){
                cc++;
                System.out.println(cc + Thread.currentThread().getName());
            }
        }
    }

    class CountClass{
        volatile int count=0;
        void countUp(){
            count++;
            System.out.println(count + Thread.currentThread().getName());
        }
    }
}

3 个答案:

答案 0 :(得分:5)

在Java中,volatile关键字的语义定义非常明确。它们确保其他线程将看到变量的最新更改。但它们不会使读取 - 修改 - 写入操作成为原子。

因此,如果ivolatile并且您i++,则可以保证读取i的最新值,并保证其他线程会看到您的i立即写入i,但不能保证两个线程不会交错读取/修改/写入操作,这样两个增量就会产生单个增量的效果。

假设i++;是一个易失整数,其值初始化为零,除此之外没有发生任何写操作,两个线程执行i,可能发生以下情况:

  1. 第一个线程读取零,即i的最新值。
  2. 第二个线程读取零,也是i的最新值。
  3. 第一个线程将其读取的零增加,得到一个。
  4. 第二个线程增加它读取的零,也获得一个。
  5. 第一个线程将计算出的一个写入i
  6. 第二个线程将计算出的一个写入i
  7. 写入i的最新值是1,因此任何访问volatile的线程现在都会看到一个。
  8. 请注意,即使每个线程始终读取任何其他线程写入的最新值,也会丢失增量。 synchronized关键字提供可见性,而非原子性。

    您可以使用Atomic*来形成复杂的原子操作。如果您只需要简单的,可以使用Java提供的各种SELECT field_b FROM table GROUP BY some_func(field_a) AS field_b 类。

答案 1 :(得分:0)

Java volatile关键字用于将Java变量标记为“存储在主存储器中”。这意味着,每次读取一个volatile变量都将从计算机的主内存中读取,而不是从缓存中读取,并且每次写入volatile变量都将写入主内存,而不仅仅是缓存。

它保证您正在访问此变量的最新值。

P上。 S.使用更大的循环来发现错误。例如,尝试迭代10e9次。

答案 2 :(得分:0)

使用volatile的用例是从映射到设备寄存器的内存中读取/写入,例如在微控制器上,除了CPU之外的其他内容将读取/写入该“内存”地址的值所以编译器不应该优化该变量。