服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服务器之家 - 编程语言 - JAVA教程 - 解析java中volatile关键字

解析java中volatile关键字

2020-03-22 12:55石头_ly JAVA教程

这篇文章主要为大家解析了java中volatile关键字,经常有人把volatile关键字和synchronized或者lock混淆,本文就为大家好好区分,感兴趣的小伙伴们可以参考一下

在java多线程编程中经常volatile,有时候这个关键字和synchronized 或者lock经常有人混淆,具体解析如下:
在多线程的环境中会存在成员变量可见性问题: java的每个线程都存在一个线程栈的内存空间,该内存空间保存了该线程运行时的变量信息,当线程访问某一个变量值的时候首先会根据这个变量的地址找到对象的堆内存或者是栈堆存(原生数据类型)中的具体的内容,然后把这个内同赋值一个副本保存在本线程的线程栈中,紧接着对这个变量的一切操作在线程完成退出之前都和堆栈内存中的变量内容是没有关系的,操作的是自己线程栈中的副本。当操作完后会把操作完的结果写回到主内存中。假如有两个线程A和B,同事操作某一个变量x;A对x进行了加1操作,那么B获取的副本可能是x加1后的结果,也可能是x;为了保证获取内存中最新的数据变量 需要加上 volatile 关键字,这样在每次对x进行操作的时候都会去检查下线程栈中的变量的值是不是和住内存中变量的值一样,如果不一样会重新load。
eg:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ThreadSee {
//t1线程会根据flag的值做对应的操作,主线程会更改t1的值
 public static void main(String[] args) throws InterruptedException {
    ThReadTest th= new ThReadTest();
    Thread t1 = new Thread(th);
    t1.start();
    Thread.sleep(1000);
    th.changeFlag();
    Thread.sleep(2000);
    System.out.println(th.getFlag());
  }
 
}
 
 
class ThReadTest implements Runnable{
 
  //线程访问变量时会把其load到对应的线程栈中,每次操作时都要获取内存中最新的数据
  private volatile boolean stopflag;
  @Override
  public void run() {
    int i=0;
    while(!stopflag){
      i++;
      System.out.println("=="+Thread.currentThread().getName());
    }
    System.out.println("Thread finish:"+i);
  }
  public void changeFlag(){
    this.stopflag=true;
    System.out.println(Thread.currentThread().getName()+"***********");
  }
 
  public boolean getFlag(){
    return stopflag;
  }
}

上述代码如果去掉volatile,会一直死循环执行下去。
但是volatile不能保证线程安全的同步
eg:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ThreadSave implements Runnable{
  static ThreadSave sync = new ThreadSave();
  static volatile int j=0;
  //Lock lock =new ReentrantLock();
  public void inscane(){
    // lock.lock();
    for(int i=0;i<10000000;i++){
      j++;
    }
   //  lock.unlock();
  }
  @Override
  public void run() {
    inscane();
  }
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(sync);
    Thread t2 = new Thread(sync);
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(j);
  }
}

根据上述代码执行的结果不是预期20000000,
因为对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的。
例如假如线程1,线程2 在进行线程栈与主内存read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值
在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;
线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;
导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。
综上所述:
volatile只会保证线程去做一个检查当前线程栈的变量值和主内存中数据值是否一样的这么一个动作,只此而已。而lock或者是synchronized 会保证某一时刻只有单个线程进入该方法,从而确保其线程安全性。
所以在如果多个线程去修改一个volatile变量那么没有实际的逻辑意义。如果一个线程去修改其他的线程依赖修改的变量值,此时是有作用的。

以上就是本文的全部内容,希望对大家的学习有所帮助。

延伸 · 阅读

精彩推荐