一、论点
第一层 保证原子性,针对64位变量的多线程操作能保证一次操作都是64位
第二层 保证了顺序一致性,不参与重排序在新的内存模型下
第三层 保证了可见性(可见性仅仅对于基本变量,如果是引用变量只能保证在赋值引用的那一刻,如果赋值后其他线程操作这个引用的变量对象 还是不能保证可见性)
二、程序说明
1. 保证原子性
publicclass Ano {
privatestaticlongnum = Long.MAX_VALUE;
publicstaticvoid main(String[] args) {
System.out.println(Long.MAX_VALUE);
System.out.println(Long.MIN_VALUE);
new Thread(){
publicvoid run(){
while(true){
long l = num;
if(l==Long.MAX_VALUE){
num = Long.MIN_VALUE;
}else{
num = Long.MAX_VALUE;
}
}
}
}.start();
new Thread(){
publicvoid run(){
while(true){
long l = num;
if(l!=Long.MAX_VALUE && l!=Long.MIN_VALUE){
System.out.println(l);
return;
}
}
}
}.start();
}
}
运行结果:
9223372036854775807
-9223372036854775808
-9223372032559808513
结论:为什么会有第三个输出呢?jvm操作内存时jvm是32位时写入数据的时候最小单位是32位,如果jvm是64位,最小操作单位就是64位。在没锁的情况下,一个线程写入64位,是分2次写的,先写高32位 在写低32位,如果刚好第一个线程在写高32位的时候 另外的线程读取这个long值,就会出错,就会读到最新的高32位,和之前老数据的低32位,就是错的的值了。所以就会出现第三个输出值。
这样就不会有问题了:privatestaticvolatilelongnum = Long.MAX_VALUE;
2. 保证了顺序一致性,不参与重排序在新的内存模型下
publicclass A {
privatestatic A a;
privateintx = 1;
privateinty = 2;
public A(int x,int y){
this.x = x;
this.y = y;
}
publicstatic A getInstance(){
if(a==null){
synchronized (A.class) {
if(a==null){
a = new A(3,4);
}
}
}
returna;
}
}
结论:第一个线程获取到a对象看到a的x是3,a的y是4, 但是如果刚好第二个线程访问,
在判断if(a==null)的时候,看到的是a不是null就获取了a对象,但是这个a对象可能还在构造,此时获取到a的时候看到的x还是1或者y还是2,这样的值就是过期的值,这就是重排序。
改成privatestaticvolatile A a;就不会有这个问题了!
3. 保证了可见性
publicclass A {
publicstaticvolatilebooleanb = false;
publicstaticintx = 1;
publicstaticvoid main(String[] args) {
new Thread() {
publicvoid run() {
boolean bb = b;
if (bb) {
System.out.println(x);
}
}
}.start();
x = 2;
b = true;
}
}
结论:X的输出值一定是2!但是如果b不是volatile则不一定,因为底层执行的情况是,main线程先把b=true写入到了内存,再把x=2写入内存的,每个线程有一个工作区间 也就是缓存区间,用来缓存数据,假如b=true先从线程缓存同步到主存,此时x=2还没有同步到主存,其他线程看到的x就是1了。加上volatile,则当b=true执行后,就会吧b=true,x=2都从缓存同步到主存中,这样x就肯定是2了。