目錄
1.volatile
1.1 volatile是什麼?
1.概念: volatile是JVM提供的一種輕量級的同步機制。
2.特性:
(1)保證可見性。Q1:什麼是可見性?
(2)不保證原子性。Q2:什麼是原子性?
(3)禁止指令重排序。Q3:什麼指令重排序?
想要理解volatile的特性,需要解決上面的靈魂三問,請繼續往下看。
1.2 JMM內存模型之可見性
1.概念:JMM是全稱是Java Memory Model ,即Java內存模型,它是一組規則或規範,用於定義程序中各個變量的訪問方式;是抽象的概念,並不真實存在。簡而言之:JMM是一種約束,用於定義內存中各個變量的讀寫方式。
2.JMM的同步規定:
(1)線程加鎖前,必須從主存中讀取共享變量的最新值到線程自己的工作內存中。Q1:什麼是線程自己的工作內存?
(2)線程解鎖前,必須將線程自己工作內存中的值刷回主存中。
(3)加鎖解鎖必須是同一把鎖。
3.線程的不可見性:
(1) 概念:線程自己的工作內存:Java程序運行的實體是線程,JVM爲每個線程都分配一個私有的內存空間(私有內存空間歸該線程獨享,其他線程不能訪問),這個私有內存空間稱之爲線程自己的工作內存(2.Q1)。
(2)線程訪問共享變量的方式:
場景:某一時刻,線程1,線程2同時訪問主存中的變量num,線程1將num的修改爲num=8。
過程:
①線程1,線程2先將主存中共享num的值拷貝一個副本到自己線程的工作內存中。此時,線程1,線程2中的副本 num = 5。
②線程1修改自己工作內存中副本 num = 8。
③線程1的副本num的值修改後,會立即刷回主存,此時主存的 num = 8。
④主存中num的值變化後,並沒有通知線程2重新從主存中拷貝num最新的值,此時,線程2中的副本 num = 5。
結論:線程1對共享變量num的修改對線程2是不可見的現象,稱之爲線程不可見性。
概念:線程對共享變量的修改,不能通知其他線程看見,稱之爲線程的不可見性,反之,線程對共享變量的修改,能夠立即通知其他線程看見,稱之爲線程的可見性。
4.JMM三大特性
(1)可見性
(2)原子性
(3)有序性
1.3 可見性的代碼驗證
(1)不可見性驗證
/**
* 驗證volatile的可見性
*/
public class VisibilityDemo {
//共享變量 5
public static /*volatile*/ int num = 5;
public static void main(String[] args) {
//開啓線程thread-1
new Thread(() -> {
//休眠2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改num的值
num = 8;
System.out.println("thread-1 修改了num的值:" + num);
}, "thread-1").start();
//開啓線程thread-1
new Thread(() -> {
System.out.println("thread-2獲取num="+num);
while (true) {
if (num != 5) {
System.out.println("thread-2 獲取了修改後的num="+num);
break;
}
}
}, "thread-2").start();
}
}
運行結果:
(2)可見性驗證
用volatile修飾變量 num : public static volatile int num = 5;
/**
* 驗證volatile的可見性
*/
public class VisibilityDemo {
//共享變量 5
public static volatile int num = 5;
public static void main(String[] args) {
//開啓線程thread-1
new Thread(() -> {
//休眠2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//修改num的值
num = 8;
System.out.println("thread-1 修改了num的值:" + num);
}, "thread-1").start();
//開啓線程thread-1
new Thread(() -> {
System.out.println("thread-2獲取num="+num);
while (true) {
if (num != 5) {
System.out.println("thread-2 獲取了修改後的num="+num);
break;
}
}
}, "thread-2").start();
}
}
運行結果:
1.4 volatile不保證原子性
(1)什麼是原子性?
原子性是指不可以分割的整體,線程處理具體業務時,處理過程中不能被加塞或者打斷,需要整體完整,業務要麼同時成功,要麼同時失敗。
(2)volatile不保證原子性代碼驗證
/**
* volatile不能保證原子性
*/
public class AtomDemo {
//字段
private int sum = 0;
/**
* 自增
*/
public void add(){
sum ++;
}
public static void main(String[] args) {
//共享變量
AtomDemo atomDemo = new AtomDemo();
//開啓20個線程
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
atomDemo.add();
}
},String.valueOf(i)).start();
}
//等待上面20個線程執行完畢,main輸出sum的值
while(Thread.activeCount()>2){ //最小兩個線程,main,gc
//如果線程數大於2,main線程讓出執行權
Thread.yield();
}
//main線程輸出sum的值
System.out.println("20個線程,每個線程加1000次,理論結果=20000?而實際結果="+atomDemo.sum);
}
運行結果: