java并发 volatile关键字的理解

在多线程的环境,当一个线程修改了共享变量,另一个线程能读取到这个变量的修改值,变量java 提供了volatile保证了变量的可见性,轻量级的synchronized
它实现的原理主要有以下两个方面

  • 追加的LOCK#指令会使处理器缓存行写回到内存
  • 一个处理器的缓存写回到内存会使其他处理器的缓存无效

volatile的应用

先看一段代码,假如线程1先执行,线程2后执行:

//线程1
boolean stop = false;
while(!stop){
    doSomething();
    }
     
  //线程2
  stop = true;

这段代码是很典型的一段代码,很多人在中断线程时可能都会采用这种标记办法。但是事实上,这段代码会完全运行正确么?即一定会将线程中断么?不一定,也许在大多数时候,这个代码能够把线程中断,但是也有可能会导致无法中断线程(虽然这个可能性很小,但是只要一旦发生这种情况就会造成死循环了)。
下面解释一下这段代码为何有可能导致无法中断线程。在前面已经解释过,每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。
那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。
但是用volatile修饰之后就变得不一样了:

  • 使用volatile关键字会强制将修改的值立即写入主存;
  • 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
  • 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
    那么在线程2修改stop值时(当然这里包括2个操作,修改线程2工作内存中的值,然后将修改后的值写入内存),会使得线程1的工作内存中缓存变量stop的缓存行无效,然后线程1读取时,发现自己的缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。
    那么线程1读取到的就是最新的正确的值

注意

volatitle保证了共享变量的可见性,但是并没有保证原子性,如果变量额的操作非原子性的,也会线程不安全。

public class Test {
    public volatile int inc = 0;
     
    public void increase() {
    inc++;
    }
              
    public static void main(String[] args) {
    final Test test = new Test();
    for(int i=0;i<10;i++){
        new Thread(){
            public void run(){
                for(int j=0;j<1000;j++)
                    test.increase();
                    };
                }.start();
            }
                                                                             
    while(Thread.activeCount()>1)  //保证前面的线程都执行完    
    Thread.yield();
    System.out.println(test.inc);
    }
}

由于自增操作是非原子操作,某一时刻,存在两个线程读取同一个有效的inc 此时由于是非原子操作,此时将进行两次++ 但是导致只发生了一次++操作。

所以volatile保证了变量的可见性但是不能不能保证线程安全

Java Current包下的原子类通过CAS(Compare and set)完成同步锁的一种乐观锁(更新时判断是否被修改) 来保证对变量的原子操作从而保证了线程安全。
如下面代码:

private volatile int value;

public final int get() {
        return value;
}
public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
        }
}

你可能感兴趣的:(java并发 volatile关键字的理解)