[网摘]CSDN上的讨论
回复1:
首先每个线程都有自己一个工作内存区,多个线程共享一个主内存区。线程中的本地变量存在自己的内存区中,如for(int i=0;i<100;i++){this.i=i;},其中i就存在线程工作内存中,即每个线程都有一个,不用也不能加volatile关键字,this.i就是共享变量。而共享的变量就存在主内存区里,但Java线程为了提高效率,会把共享变量拷贝到自己的工作区中,这就产生了变量一致性的问题。
java提供的一种方法是互斥访问,互斥访问会在加锁和解锁中维持变量的一致性,另一种就是volatile关键字。
java language specification中的一个例子,有类如下:
class Test {
static int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
有两个线程,一个不停调用 one(),一个不停调用 two(),则有可能出现这种情况,打印出来的j比i还大。因为这时线程对共享变量的更新是无序的。
1.使用同步方法:
class Test {
static int i = 0, j = 0;
static synchronized void one() { i++; j++; }
static synchronized void two() {
System.out.println("i=" + i + " j=" + j);
}
}
这就不用我介绍了,i和j始终一样大。
2.使用volatile
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
这样能允许one()和two()并发执行,同时使one()如字面一样执行。这一般能使打印出来的j不会大于i,在更新j之前会先更新i。但有可能打印出来的j比i大很多,因为one()可能在two获取i和j之间执行了很多次。
说明一下:
java language specification中的例子不好,有点晦涩,但我认为volatile的作用就是保证任何时候主内存的i都大于等于j。two()中出现j比i大很多,只是因为它访问的是不同时刻的主内存。
还有一点,就是volatile能防止编译器对变量进行优化,每次共享变量都到主内存。如果线程不到主内存中读,变量的值就会不正确。
回复2:
更确切一点的说,在线程改变其自身工作区中的volatile变量时,会强制立即将该值更新到主工作区中,从而保证数据的一致性。synchronized和volatile结合使用能保证多线程的完全同步。
回复3:
还有遗漏了一点没说,线程在试图读取一个volatile变量时,会从主内存区中读取最新的值