qiezijiajia

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

当将变量声明为volatile时,对这个变量的单个读/写相当于加了锁,但是多个读/写则不是,比如i++,下面看例子:

public class VolatileTest {

    volatile int i=0;

    public int get(){   //单个操作,相当于加了synchronized关键字

        return i;
    }

    public void set(int tmp){  //单个操作,相当于加了synchronized关键字

        i=tmp;
    }

    public void getAndIncrement(){ //非单个操作,i++,相当于先get(),再加1,再set(); 所以在多线程操作时,如果某个线程get()后,还没有进行set操作,另外一个线程又get()了, 这样导致两个线程get到同一个值。

        i++;
    }


    public static void  main(String[] args){

        final VolatileTest volatileTest=new VolatileTest();
        for(int i=0;i<=100;i++){

            new Thread(){

                public void run(){

                    volatileTest.getAndIncrement();
                }
            }.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(){

                public void run(){

                    System.out.println(volatileTest.get());
                }
            }.start();
        }


    }

下面是上面程序的输出: 可以看到某些数字是重复的。

1
2
3
4
5
6
7
8
9
10
12
12
13

.

.

.



Volatile变量具有以下特性:

1.可见性,任何一个线程对该变量的读操作,总能看到任意线程对这个变量最后的写操作;

2.原子性:对任意单个操作的读/写具有原子性,单对于多操作如i++这种复合操作不具有原子性;

 

内存可见性:

写内语义:

volatile写的内存语义如下:

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中去。

以上面示例程序VolatileTest为例,假设线程A首先执行writer()方法,随后线程B执行reader()方法,初始时两个线程的本地内存中的flag和a都是初始状态。下图是线程A执行volatile写后,共享变量的状态示意图:

 

volatile读的内存语义如下:

  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来会从主内存中读取共享变量。

下面是线程B读同一个volatile变量后,共享变量的状态示意图:

 

posted on 2017-08-02 15:25  qiezijiajia  阅读(156)  评论(0编辑  收藏  举报