最近在读一本书《Java并发编程的艺术》,书中关于被volatile修饰的变量有如下特性:
可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
关于上述对volatile可见性的结论,我做了一下验证,发现并不是这么回事。具体代码如下:
public class VolatileRule {
private volatile Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static void main(String[] args) {
final VolatileRule obj = new VolatileRule();
obj.setAge(10);
ExecutorService executors = Executors.newFixedThreadPool(10);
executors.execute(new Runnable() {
@Override
public void run() {
System.out.println(obj.getAge());
obj.setAge(20);
}
});
executors.execute(new Runnable() {
@Override
public void run() {
System.out.println(obj.getAge());
obj.setAge(30);
}
});
System.out.println(obj.getAge());
}
}
上述代码执行结果有时候会20,有时候为30。跟前面的结论有冲突,我也不清楚是我理解错了,还是测试代码写的又问题。求各位大神解惑。
大兄弟,你这不对啊,你用线程池做测试,线程具有独立的运行能力,也就是你最后一句打印的时候,两个线程池都有可能没有执行完!
如果最后一句打印执行的时候,第一个线程池还没有执行完,则输出10,如果第一个执行完了,第二个还没有执行完,则输出20,如果第二个
也执行完了,则输出30,这就是为什么让你感觉到volatile没有体现到他的内存可见性的原因。
你可以将三个打印都加上标记比如:
ExecutorService executors = Executors.newFixedThreadPool(10);
executors.execute(new Runnable() {
@Override
public void run() {
obj.setAge(20);
System.out.println("20 set==="+obj.getAge());
}
});
executors.execute(new Runnable() {
@Override
public void run() {
obj.setAge(30);
System.out.println("30 set==="+obj.getAge());
}
});
System.out.println("final set==="+obj.getAge());
看一下三个打印执行的顺序
不对吧,volatile可见性是任何线程的写对其他线程是可见的。但是我线程A写入了volatile变量,线程B有时候也读不到。
public class VolatileRule {
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static void main(String[] args) throws InterruptedException {
final VolatileRule obj = new VolatileRule();
obj.setAge(10);
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("10---" + obj.getAge());
obj.setAge(20);
System.out.println("11---" + obj.getAge());
}
});
System.out.println("final1:" + obj.getAge());
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("20---" + obj.getAge());
obj.setAge(30);
System.out.println("21---" + obj.getAge());
}
});
threadA.start();
threadB.start();
threadA.join();
threadB.join();
System.out.println("final2:" + obj.getAge());
}
}
1、首先明确一点,volatile修饰的变量只有可见性,不具有原子性;所以不是线程安全的。
2、一个线程对一个变量的操作都是在本线程的工作空间进行的而不是内存(这里指虚拟机的)。
3、一个线程对变量的操作在字节码层面分别为:read-load-use-asign-store-write。
4、在read操作中 volatile保证此变量的值是这个时刻最新的,赋值到当前线程工作空间的对应变量。
5、所以当两个线程同时执行,都读取了内存某一变量时候,接下来的操作就会引发线程安全问题。
综上 volatile的可见性是在read环节,至此之后没有可见性一说