java学习之路

java高并发编程(二)volatile关键字机制

2020-02-16  本文已影响0人  唯有努力不欺人丶

volatile关键字

volatile是java中的一个关键字,使一个变量在多个线程之间可见。
这里有个很有意思的demo可以让我们更好的知道变量值的可见机制,我先贴上代码:

package thread;

import java.util.ArrayList;
import java.util.List;

public class VolatileDemo {
    List<Integer> list = new ArrayList<Integer>();
    
    public void add(Integer i) {
        list.add(i);
    }

    
    public static void main(String[] args) {
        VolatileDemo vd = new VolatileDemo();
        new Thread() {
        @Override
        public void run() {
            System.out.println("T1 start...");
            for(int i = 0;i<10;i++) {
                vd.add(i);
                System.out.println(vd.list.size());
            }
            System.out.println("T1 end...");
        }}.start();
        
        new Thread() {
        @Override
        public void run() {
            while(true) {
                if(vd.list.size()==5) {
                    System.out.println("list.size() = 5,T2 end...");
                    break;
                }
            }
        }   
        }.start();
    }

}

如上代码所示,正常的理解,是不是应该随着t1线程不断往里添加,添加到5个的时候t2进入到while的if里,然后输出语句,然后break。按理说剧本应该是这么演的。但是!!!问题来了,剧本是理想情况,跟实际是不符合的,不信可以自己运行以下,我顺便附上一个运行截图:


运行截图

如图,其实这个线程1正常运行了,但是线程2就没进过if里。这是为啥呢?按理说都是一个vd对象,而且vd里的元素填充也是认真的,但是为什么就不进到if呢?
这个和java的线程中变量引用机制是有关系的。A,B线程都用到一个变量以后,java默认是A线程中保留一份变量的copy。这个可能放在线程的缓存区中(具体放在那里我也不是那么清楚,这个我也只是知道大概是缓存区。没细研究过)。以后线程每次使用这个变量都是直接去缓存中读取,B线程如果也使用也会copy一份,如果更改了会通知给主内存中,然后主内存中会改变这个变量。但是!!!不做任何处理的时候(如上代码)那么A一直用的都是最开始读的值。所以以上的代码永远也进不了T2的if中。

volatile怎么工作的?

回归正题,咱们最开始就说了volatile的作用就是使变量在多个线程中可见。我去更改上面代码添加个volatile试试。


如图,按预期运行

当然了,这段代码是有问题的,因为我们希望是size是5的时候正好弹出来等于5,但是两个线程是并行的,其实可能都已经添加到10了才弹出这句话:


image.png
但是起码说明这个volatile的作用确实是保证变量改变后确实是在多个线程间可见了,接下来我会画个草图大概的说以下volatile的原理(这个图我觉得画的很形象啊):
volatile的作用示意图
volatile的作用

刚刚其实一直都在讲volatile的作用,这里不过是再做一个总结和整理
当数值发生变化主动通知所有调用的线程,而线程接到通知会重新读取数据。由此volatile保证了数据的可见性。

但是!!!注意了,volatile只能保证可见性,而不能保证多个线程共同修改变量时所带来的不一致问题。说个最简单的比方,就是多个线程同时将一个count++.那么最终的出来的结果大概率是不准确的。可能两个线程读取到了相同值,然后都加了1.因为count++不是原子性的,所以在+的过程中别的线程都更改完了。其实这个是最简单的一个情况。

只要记住一句话:
volatile只能保证可见性不能保证原子性。而synchronized既能保证可见性又能保证原子性。但是synchronized性能上因为排队操作,所以大大的受影响。
synchronized有个使用的标准:锁尽量少的代码,因为细粒度的锁比粗粒度的锁性能高。
然后关于volatile的知识点就这么多了。赶明有空整个在一起再说。其实这个关键字是要结合实际情况使用的。什么时候互斥锁,什么时候volatile,什么时候atomic,什么时候出入锁啥的。明天打算整理一个小面试题。今天就到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!生活健健康康!

上一篇下一篇

猜你喜欢

热点阅读