volatile关键字与内存可见性

内存屏蔽

在某些情况下多个线程共享的数据段,会出现内存屏蔽,即其中一个线程已经对该共享数据做了修改,可其他线程却一直没有进行数据更新(比如在使用while(true){}循环时,由于此结构调用了较为底层的代码,执行效率极高,以至于没有更新数据段的机会)

看个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package juc;

public class Main {

public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadDemo td=new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlag()){
System.out.println("-------------");//此段代码无法被执行
break;
}
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package juc;

public class ThreadDemo implements Runnable{
private boolean flag=false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag=true;
System.out.println("flag="+isFlag());
}

}

输出结果如下

解决方法

  1. 同步锁(效率很低)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    while(true){
    synchronized (td){//可以保证数据的一个及时更新
    if(td.isFlag()){
    System.out.println("-------------");
    //此段代码现在可以被执行
    break;
    }
    }
    }
  2. 使用volatile关键字,当多个线程操作共享数据时,可以保证内存中的数据可见,为实例域的同步访问提供了一种免锁机制

    1
    private volatile boolean flag=false;

    不过要注意的是,volatile不具备“操作互斥性”,且不提供原子操作

Donate comment here