代码一:
如下代码开启2个线程,一个只读,一个只写。
可以看到写线程虽然改变了共享变量,但是只读线程似乎根本不care变化,输出的变量一直是0。
package com.example.multithread.volatiletest;
import java.util.stream.IntStream;
public class VolatileReadOnly {
// private volatile static int INIT_VALUE=0;
private static int INIT_VALUE=0;
private final static int MAX_VALUE=10;
public static void main(String[] args) {
//ReadOnly thread cache
new Thread(()->{
int currentValue=INIT_VALUE;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
IntStream.rangeClosed(1,5).forEach(i->System.out.println(i+":"+currentValue));
},"reader only").start();
//write thread cache
new Thread(()->{
while(INIT_VALUE<MAX_VALUE){
INIT_VALUE++;
System.out.println(Thread.currentThread().getName()+": "+INIT_VALUE);
}
},"writer only").start();
}
}
执行结果:
writer only: 1
writer only: 2
writer only: 3
writer only: 4
writer only: 5
writer only: 6
writer only: 7
writer only: 8
writer only: 9
writer only: 10
reader only:0
reader only:0
reader only:0
reader only:0
reader only:0
代码二:
如下代码,一个只写线程,一个读写线程。
package com.example.multithread.volatiletest;
public class VolatileReadAndWrite {
// private volatile static int INIT_VALUE=0;
private static int INIT_VALUE=0;
private final static int MAX_VALUE=10;
public static void main(String[] args) {
//write thread cache
new Thread(()->{
while(INIT_VALUE<MAX_VALUE){
System.out.println(Thread.currentThread().getName()+": "+INIT_VALUE);
++INIT_VALUE;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"writer only").start();
//read and write cache
new Thread(()->{
int currentValue=INIT_VALUE;
while(currentValue<MAX_VALUE){
System.out.println(Thread.currentThread().getName()+": " + INIT_VALUE);
currentValue=++INIT_VALUE;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"reader and writer").start();
}
}
执行结果:
writer only: 0
reader and writer: 1
reader and writer: 2
writer only: 2
reader and writer: 4
writer only: 4
reader and writer: 6
writer only: 6
reader and writer: 8
writer only: 8
reader and writer: 10
1.如上是JMM简图,cpu执行计算时,会将主内存中的数据复制一份到cpu缓存中,后续操作与缓存交互(cpu与主内存速度差很大,与cpu缓存速度差距不大)。
2.代码一中的读线程由于只有只读动作,cpu后续的计算操作只会与cpu缓存交互,缓存中的数据是程序开始执行时主内存的一个副本,写线程写回主内存的数据并未影响到读线程缓存数据。
3.代码二中的读写线程由于有写的动作,该线程会去主内存中重新加载最新数据到cpu缓存中。输出结果也是不连续的,说明写线程同步回主内存的结果已经被读写线程获取到。
4.为了保证共享变量被一个线程修改后,别的线程可以读取中最新的数据,需要在共享变量前加上volatile关键字。这样共享数据发生改变时,写线程会通过总线通知使用该共享变量的缓存失效,这样cpu在需要使用到共享变量时会去主内存中重新加载并存到cpu缓存中。volatile保证了共享变量在不同线程间的修改可见性。