说说你对原子性、可见性、有序性的理解?
原子性、有序性、可见性是并发编程中非常重要的基础概念,JMM的很多技术都是围绕着这三大特性展开。
线程切换会带来原子性问题,示例:
int count = 0; //1
count++; //2
int a = count; //3
上面展示语句中,除了语句1是原子操作,其它两个语句都不是原子性操作,下面我们来分析一下语句2
其实语句2在执行的时候,包含三个指令操作
- 指令 1:首先,把变量 count 从内存加载到 CPU 的寄存器
- 指令 2:然后,在寄存器中执行 +1 操作
- 指令 3:最终,将结果写入内存
对于上面的三条指令来说,如果线程 A 在指令 1 执行完后做线程切换,线程 A 和线程 B 按照下图的序列执行,那么我们会发现两个线程都执行了 count+=1 的操作,但是得到的结果不是我们期望的 2,而是 1。
- 可见性:指一个线程对共享变量的修改,对于其他线程应该是立即可见的,确保了各个线程之间对内存状态的正确观察。
- 有序性:指程序执行的顺序按照代码的顺序执行。在单线程的情况下,代码执行顺序与编写的顺序一致。但在多线程环境中,由于时间片轮换,不同的线程可能会交替执行不同的代码。
原子性、可见性、有序性都应该怎么保证呢?
- 原子性:JMM只能保证基本的原子性,如果要保证一个代码块的原子性,需要使用synchronized。
- 可见性:Java是利用volatile关键字来保证可见性的,除此之外,final和synchronized也能保证可见性。
- 有序性:synchronized或者volatile都可以保证多线程之间操作的有序性。