volatile、Synchronized实现变量可见性的原理,volatile使用注意事项

变量不可见的两个原因

       Java每个线程工作都有一个工作空间,需要的变量都是从主存中加载进来的。Java内存模型如下(JMM):

线程访问一个共享的变量时,都需要先从主存中加载一个副本到自己的工作内存中,经过自己修改后再更新到主存中去。在这个过程中可能出现这种情况:线程A在工作内存中修改了变量1的值,但是还没有写入主存,这档口线程B将变量1加载到自己工作内存中。显然,线程B拿到的不是变量1的最新值了。

        变量可见性就是: 这个变量被任何一个线程修改了,其他线程都能“看见”,也就是能取到变量最新的值。

        重排序是指为了适合cpu指令执行机制,编译器、内存系统、处理器可能会对一些指令的执行顺序进行重排。例如:

int a = 1;               //line1
int b = 2;                //line2
int s = a*b;        //line3

line1 和 line2 可能会被重排颠倒位置,但是line3不会重排,因为Java单线程下会遵守 as-if-serial语义,简单的讲就是重排指令不会出现错误的结果。在多线程下,指令重排则可能造成一些问题,例如:

class Example {
int a = 0;
boolean flag = false;
 
public void writer() {
    a = 1;                   
    flag = true;           
}
 
public void reader() {
    if (flag) {                
        int i =  a +1;      
    }
}
}

线程A首先执行writer()方法,线程B线程接着执行reader()方法。线程B在int i=a+1 是不一定能看到a已经被赋值为1,因为在writer()中,两句话顺序可能打乱:

线程A执行顺序:  flag=true;a=1;

线程B执行:flag=true  (而此时a=0)    产生了一些与我们预期之外的情况。

导致变量不可见的原因(1)更新不及时,(2)多线程交替执行时的指令重排序

volatile实现可见性的原理

     解决第一个导致不可见的因素(更新不及时):内存屏障,对于volatile修饰的变量,读操作时使用load指令重新从主存加载最新值进来;写操作时使用store指令将最新的值立刻写入主存。

     解决第二个因素(指令重排): volatile修饰的变量禁止指令重排序。

Synchronized实现可见性原理

     解决第一个因素:在加锁前会将工作内存的值全部重新加载一遍,保证最新;释放锁前将工作内存的值全部更新到主存;由于在带锁期间,没有其他线程能访问本线程正在使用的共享变量,这样就保证了可见性。

     解决第二个因素: 由于Synchronized修饰的代码块都是原子性执行的,即一旦开始做,就会一直执行完毕,期间有其他线程不可以访问本线程所使用的共享变量,这样,即便指令重排了也不会出现问题。

volatile不具有原子性可能导致的问题

        经过前面的总结,可以看出Synchronized包裹的代码里的共享变量有可见性、指令不可排序性、原子性;volatile修饰的变量有可见性、指令不可排序性;volatile并不保证变量有原子性。如果用volatile修饰变量希望保证它的原子性就可能出现问题,例如:

volatile  int num = 0;

线程A:
      num++;

线程B:
      num++;

两个线程操作都进行num++的操作,理论上完事后 num 的值为2,但是num++ 的操作本身不是原子性的(包括读取 num原先的值、+1、把+1后的值写入num),volatile也不能使对num的操作变为原子性。因此可能有num = 1的结果:

       线程A:读取了num的值,为0,然后阻塞了。

       线程B:读取num的值,还是为0,+1后立即写入主存,num = 1(体现可见性)。

        线程A:已经做完读取操作了,并且工作内存中num = 0,继续执行。num+1,写入内存,num = 1;

虽然这种情况发生的概率很小,但是并发量大时还是会出现不少这种情况的。(可以用Synchronized、lock、AtomicInteger等解决)

        由于原子性的问题,volatile不适合修饰依赖自身的变量 :  num++、a = a*2...    也不适合修饰不变式的变量(即volatile变量应该是独立的):a<b.... 

猜你喜欢

转载自www.cnblogs.com/shen-qian/p/11250805.html