synchronized 能夠保證在同一時刻最多只有一個線程執行該段代碼,以達到保證並發安全的效果;
synchronized(鎖對象) { // 受保護資源; }
目標:學習使用 synchronized 保證原子性的原理
使用 synchronized 保證原子性
案例:5個線程各執行10000次 i++
package juc.synchronized_test.prove; import java.util.ArrayList; import java.util.List; /** * @author : 雪飛oubai * @date : 2020/4/9 11:27 * 目標:演示原子性問題 * 1、定義一個共享變量 number * 2、對number進行1000次 ++ 操作 * 3、使用 5 個線程來進行 */ public class Test02Atomicity { //1、定義一個共享變量 number private static int number = 0; private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException { Runnable increment = () -> { for (int i = 0; i < 10000; i++) { synchronized (obj) { number++; } } }; List<Thread> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { Thread t = new Thread(increment); t.start(); list.add(t); } for (Thread thread : list) { thread.join(); } System.out.println("number=" + number); } }
synchronized 保證原子性的原理
對 number++;增加同步代碼快,保證同一時間只有一個線程操作 number++;。就不會出現安全問題;
synchronized 與可見性
目標:學習使用 synchronized 保證可見性的原理
使用 synchronized 保證可見性
案例演示:一個線程根據 boolean 類型標記 flag,while循環,另一個線程改變這個flag變量的值,另一個線程不會停止循環
package juc.synchronized_test.prove; import java.util.concurrent.TimeUnit; /** * @author : 雪飛oubai * @date : 2020/4/9 11:07 * 目的:演示可見性問題 * 1、創建一個貢獻變量 * 2、創建一條線程不斷的讀取共享變量 * 3、創建一條線程修改共享變量 */ public class Test01Visibility { // 多個線程都會訪問的數據,我們稱為線程的共享數據 private static boolean flag = true; private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (flag) { synchronized (obj) { } } }).start(); TimeUnit.SECONDS.sleep(1); new Thread(() -> { flag = false; System.out.println("時間到,線程2設置為false"); }).start(); } }
小結:
synchronized 保證可見性的原理,執行 synchronized時,會對應執行 lock 原子操作會刷新工作內存中共享變量的值;
synchronized 與有序性
目標:學習使用 synchronized 保證有序性的原理
為什么要重排序
為了提高程序的執行效率,編譯器和CPU對象程序中的代碼進行重排序;
as-if-serial語義
as-if-serial語義的意思是:不管編譯器和CPU如何重排序,必須保證單線程情況下程序的結果是正確的;
以下數據有依賴關系,不能重排序
寫后讀
int a = 1; int b = a;
寫后寫
int a = 1; int b = a;
讀后寫
int a = 1; int b = a; int a = 2;
編譯器和處理器不會存在數據依賴關系的操作做重排序,因為這種排序會改變執行結果。但是,如果操作之間不存在依賴關系,這些操作就可能被編譯器和處理器重排序;
int a = 1; int b = 2; int c = a + b;
上面 3 個操作的數據依賴關系如圖所示:
如圖所示 a 和 c 之間存在數據依賴關系,同時 b 和 c 之間也存在數據依賴關系。因此在最終執行的指令序列中,c 不能被重排序到 a 和 b 的前面。但是 a 和 b 之間沒有數據依賴關系,編譯器和處理器可以重排序 a 和 b 之間的執行順序;下圖是該程序的兩種執行順序;