先说结论不能:
代码实测下:
public class Counter {
public volatile static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
}
执行环境:jdk1.8.0_372 CPU:intel i7-4960 2.6GHz
执行结果:
volatile不是可以保证线程从主存读取最新数据吗,为什么volatile解决不了多线程计数问题?
-
例如假如线程A,线程B在进行read,load 操作时,发现主内存中count的值都是5,那么都会加载这个最新的值。
在线程A对count进行修改之后,会write到主内存中,主内存中的count变量就会变为6。 -
由于count被volatile修饰,其他线程保存的count值作废。这个过程其实是MESI协议。Modify,Exclusive,Shared,Invalid. 当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
-
所以,线程B更新自己的缓存值变为6,再加1变为7,这不是没有问题吗?问题出在哪里?
-
cout++
可以拆分成三步:1、线程读取i 2、temp = i + 1 3、i = temp
当 i=5 的时候A,B两个线程同时读入了 i 的值, 然后A线程执行了 temp = i + 1的操作, 要注意:此时的 i 的值还没有变化,然后B线程也执行了 temp = i + 1的操作。注意,此时A,B两个线程保存的 i 的值都是5,temp 的值都是6, 然后A线程执行了 i = temp (6)的操作,i的值会立即刷新到主存,并通知其他线程保存的 i 值失效。 之后B线程需要重新读取 i 的值,重新读取后B线程保存的 i 变成了6,B线程保存的 temp 也是6, 然后B线程执行 i=temp (6),所以导致了计算结果比预期少了1。
深度剖析Java的volatile实现原理,再也不怕面试官问了
为什么volatile能保证有序性不能保证原子性
volatile关键字并不能作为线程计数器
Volatile与多线程