1、數據一致性如何保證 (線程的安全和線程同步)
線程安全在三個方面體現:
1.原子性:提供互斥訪問,串行線程(atomic,synchronized);
2.可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
3.有序性:一個線程觀察其他線程中的指令執行順序,由於指令重排序,該觀察結果一般雜亂無序,(happens-before原則 A操作一定在B操作之前,而是A操作的影響能被操作B觀察到)
2、synchronized (線程互斥)
synchronized可以修飾在 普通方法中、靜態方法中、代碼塊,
synchronized是內置的語言實現,jvm編譯器(monitor)去保證鎖的加鎖和釋放 ,synchronized在發生異常時,會自動釋放線程占有的鎖,因此不會導致死鎖現象發生
解決多線程並發訪問共享數據的競爭問題
特點:
保證了原子性、可見性、有序性
可重入鎖 既同一線程中內部方法推出不用再進入該線程自動獲取鎖
synchronized使用的鎖對象是存儲在Java對象頭的Mark Word內,Mark Word存儲對象的HashCode、分代年齡、鎖
其中鎖分為:偏向鎖、輕量級鎖、自旋鎖
jdk1.6以后對synchronized做了優化
一個線程獲得了鎖,進入偏向模式,當這個線程再次請求鎖時,無需再做任何同步操作,即可獲取鎖,但鎖競爭比較激烈的場合,偏向鎖就失效
轉換為輕量級鎖,不存在競爭, 輕量級鎖失敗后
轉換為自旋鎖,若干次循環后 去競爭鎖
3、ReentrantLock(可重入鎖)
ReentrantLock是JDK自帶的,實現了Lock接口
比synchronized更加靈活,支持一個線程對資源重復加鎖,需要手動釋放鎖, 可以查看獲得鎖的狀態 也可以中斷鎖(lockInterruptibly()),同時也支持公平鎖與非公平鎖
公平與非公平指的是在請求先后順序上,先對鎖進行請求的就一定先獲取到鎖,那么這就是公平鎖,反之就是非公平鎖 (默認使用非公平鎖)
ReentrantLock有3個內部類,分別是Sync、NonfairSync、FairSync ,其中Sync繼承AQS框架核心類,而NonfairSync(非公平鎖)、 FairSync(公平鎖)則繼承自Sync
AQS的內部類 獲取鎖時 線程獲取鎖成功時,對同步狀態執行CAS操作,嘗試把state的狀態從0設置為1,
獲取鎖失敗時,AQS會將該線程以及相關等待信息包裝成一個節點(Node)並將其加入同步隊列
公平鎖在線程請求到來時先會判斷同步隊列是否存在結點,如果存在先執行同步隊列中的結點線程,當前線程將封裝成node加入同步隊列等待
非公平鎖當線程請求到來時,不管同步隊列是否存在線程結點,直接嘗試獲取同步狀態,獲取成功直接訪問共享資源
AQS還有Condition內部類等待隊列。。。
4、volatile
變量定義為 volatile 之后 具備兩種特性:保證此變量對所有的線程的可見性;禁止指令重排序優化
一、可見性:保證了新值能立即同步到主內存,以及每次使用前立即從主內存刷新
多線程給變量賦值會出現線程安全問題i++;操作並不具備原子性
例 T1.load(i)
T1.sore(i+1) 被緩存鎖定其他CPU無法讀寫i (MESI緩存一致性協議) , T2的緩存區數據無效了 執行 + 1操作 返回結果還是1
二、volatile禁止重排優化 (有序性)
為了提高性能,編譯器和cpu處理器的常常會對指令做重排 ,可能無法保證多線程變量的一致性
volatile變量通過內存屏障是一個CPU指令,指令間插入一條Memory Barrier則會告訴編譯器和CPU,不管什么指令都不能和這條Memory Barrier指令重排序
5、atomic
unsafe cas
6、ThreadLocal
ThreadLocal會為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
它只是一個線程的局部變量(其實就是一個Map)(以空間換時間)