Java中volatile的作用以及它和synchronized的比较

来源:互联网 发布:linux简化命令 编辑:程序博客网 时间:2024/06/07 18:19
volatile不能保证操作的原子性。synchronized可以保证操作的原子性。我认为这是他们最大的区别。
此外 volatile是变量修饰符,而synchronized是要修饰一段代码或者方法

由java的内存模型可以知道,对于多线程虚拟机将内存分为工作内存和主内存。每一个线程都拥有一个工作内存(线程的工作内存往往对应着告诉缓存)。在工作内存中各个县城会放内存中变量的一个拷贝。
比如主内存总有变量 i = 1; 线程A如果需要使用数据i就需要用虚拟机提供的read方法从主内存中读取变量i并用load方法放入A的工作内存中。这样A就可以对i进行操作了。如果现在A需要改变i的值,比如让 i=2;这个时候需要使用store把新的i的值传到主内存,再用wtite将i=2写到主内存中。但是这个操作并不是实时进行的。
那么假如现在还有一个线程B,它也从主内存read/load了i的值,并且它也修改了i的值为i=3;这个时候就会出现主内存和线程A或B都不一样。当A用store/write将自己修改的值传给了主内存,这时主内存中i=2,线程B中i=3.

那么volatile是干什么的呢?
volatile是变量修饰符。被它修饰的变量不会在线程中保留备份(注:可以这么理解,但是其内部机制应该还是保留了备份的,只不过是通过特殊的机制实时的将线程中变量的值更新到i)。这样就不会出现上述线程A B 和主内存的值不一样的情况。
但是注意:volatile并不能保证操作的原子性。即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,依然可能出现写入脏数据的情况。它只不过是保证各个线程操作的是同一块儿内存而已。

synchronized为什么能保证操作的原子性呢?
这得从它的工作机制说起,很明显synchronized能够保证被它修饰的一段代码同一个时刻只被同一个线程执行。
事实上它同步代码的过程也在同步内存。
加入有如下代码:
public class Test implements Runnable{
      private int i=1;
      public void run(){
          operate();
      }

      public synchronized void operate(){
            i = 2;
     }
}

当线程A执行方法operate的时候将进行如下步骤:
1.线程请求this的锁,如果锁被其他的线程占用就进入阻塞状态直到锁被释放。
2.线程内存中缓存的数据被清除,从主内存中read/load读入最新的值。
3.synchronized内的代码块被执行,在此执行过程中其对应的主内存不会被其他的线程使用。
4.执行完毕释放锁。
0 0