1. 簡介
我常常使用synchronized和volatile在我們的程序中保證在并發(fā)中數(shù)據(jù)的安全,但是我們怎么理解他們了??
2.JMM(Java Memory Model)
JMM 是一個JVM對主內(nèi)存和緩存和CPU之間的關(guān)系做了簡化抽象,在實際情況下并不存在.在下圖 ThreadStack 對應(yīng)緩存和CPU的寄存器,而Heap對應(yīng)主內(nèi)存
image.png
3. 異同
- Synchronized 是修飾代碼塊和方法的,而volatile是修飾變量的
- Synchronized 是保證原子性和有序性和可見性,而volatile只有 有序性和可見性
3 Synchronized 會導(dǎo)致線程上下文切換,而 volatile不會,這就會使volatile的效率高于synchronized- synchronized是在讀入變量的時候,會清空當(dāng)前線程的緩存,直接從主內(nèi)存中讀取,而volatile是在其中有一個線程進(jìn)行寫操作結(jié)束的時候,會通知其他線程他們緩存失效,需要從主內(nèi)存中讀取,用volatile修飾的變量所有的寫操作都優(yōu)先于讀操作。(任何變量進(jìn)行寫操作的時候都會寫入主內(nèi)存,而讀取就優(yōu)先從緩存中讀取)
4 一個簡單的例子
這個例子我們會在多線程下,執(zhí)行對同一個變量進(jìn)行修改和讀取
4.1 我對寫操作使用synchronized 和讀操作不使用,變量也不用volatile修飾
package liusheng.main.liusheng.main;
import liusheng.main.ConcurrentQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class SynAndVolatile {
static class TestClass {
int num = 0;
public synchronized void add() {
num++;
}
public int getNum() {
return num;
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(20);
TestClass testClass = new TestClass();
IntStream.range(0, 10).boxed().map(i -> (Runnable) () -> {
for (int j = 0; j < 10000; j++) {
testClass.add();
}
System.out.println("完成");
}
).forEach(executorService::execute);
for (; ; ) {
if (testClass.getNum() == 100000) {
System.out.println("SUCCESS");
}
}
}
}
4.1 結(jié)果

image.png
我們發(fā)現(xiàn)程序卡主了,讀取不出num的值,也就是main線程是在緩存中讀取的,不是在主內(nèi)存中讀取的,所以發(fā)現(xiàn)不了num值的變化
4.2 我們對num進(jìn)行volatile修飾,其他的還是一樣的
volatile int num = 0;
4.2 結(jié)果

我們發(fā)現(xiàn)主內(nèi)存檢查到了num值發(fā)生的變化,當(dāng)num值發(fā)生變化的時候會通知其他(含有num副本的現(xiàn)場)線程,這num副本失效,需要從主內(nèi)存從新讀取。
