在JAVA的语言规范中指出,为了获得最佳的速度,允许线程保存共享成员变量的私有拷贝,只有当线程进入或是离开同步代码块时才与共享成员变量的原始值对比,如果不同则修改为新的值。
以上的规范说明了,每个线程在操作共享成员变量的时候是先将它处理为线程内的一个私有变量,这样每个线程在处理对应的共享成员变量时是互不影响的,而且在处理完成后将值回写到真实的成员变量上。这个过程及易导致同步问题。
之前我们讨论了通过synchronized方式来解决该问题。但是JAVA还提供了一个修饰关键字volatile。被该关键字修饰的变量每次被线程访问时,都强迫从共享内存中重读该变量的值,并在变量发生变化时,强迫线程将变化值回写到共享内存中。这样相当于线程要实时的去取得共享成员变量,减少了旧值覆盖新值的情况。这就相当于提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
这样做存在的问题就是,效率会较原先有所降低。尽量少用吧。
当时这里还有一个问题需要我们注意一下,volatile只能保证线程取到的共享成员变量是最新的值,但是线程取到值后如果共享成员变量的值发生了变化,变化后的值是不会影响到线程中的临时值的。如下程序
public class VolatielTest {
static class NumberAdd implements Runnable{
private volatile int number = 0;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("exception");
}
number++;
System.out.println("Number value is : "+ number);
}
}
public static void main(String[] arr ){
NumberAdd numberAdd = new NumberAdd();
for(int i=0;i<100;i++){
Thread thread = new Thread(numberAdd);
thread.start();
}
}
}
可以假设一下结果后再实际的执行一下,结果很出乎意料。可见volatile只能保证每次可以取到最新值,但是取到值后就不管了,所以这个关键字不能用在值频繁发生变化的情况,否则的话天才知道会发生些什么事情。