要解決執行緒不安全問題,首先要了解導致執行緒不安全的因素:
- CPU是搶占式執行的(萬惡之源)
- 多個執行緒同時修改同一個變數(即共享變數)
- 可見性
- 原子性
- 指令重排序
1、2兩種因素,是我們無法改變的,所以我們只能解決剩下的因素,
解決方案
-
用volatile修飾
解決可見性:
每個執行緒都有自己的作業記憶體,假設有兩個執行緒,對一個count值進行修改,按一般情況是執行緒1對count修改為1再放入主記憶體,執行緒2再對count修改為-1,再存入主記憶體,但此時執行緒1的作業記憶體中count還是為1(即執行緒2的修改對執行緒1是不可見的),但是對count進行volatile修飾,所有的執行緒再修改完成之后就會強制清除掉自己作業記憶體中的count,之后所有執行緒在自己作業記憶體中就找不到count了,就都會去找主記憶體的值,從而解決了記憶體不可見問題,解決指令重排序問題:禁止了指令重排序
-
java中兩種加鎖(synchronized 和 Lock)
java中加鎖
首先要先了解操作鎖的流程:
①嘗試獲取鎖;
②使用鎖;
③釋放鎖,
我們主要關注獲取鎖和釋放鎖,
1. synchronized(jvm層的解決方案)
synchronized的底層是使用作業系統的mutex lock實作的,
當執行緒釋放鎖時,JMM(java記憶體模型)會把該執行緒對應的作業記憶體中的 共享變數重繪到主記憶體中;
當執行緒獲取鎖時,JMM會把該執行緒對應的本地記憶體置為無效,從而使得被監視器保護的臨界區代碼必須從主記憶體中讀取共享變數,
synchronized用的鎖是存在Java物件頭里的
synchronized的3種使用場景:
①可以修飾代碼塊(可給任意物件加鎖)
②可以修飾靜態方法(對當前的類進行加鎖)
③可以修飾普通方法(對當前類實體進行加鎖)
2. Lock手動鎖
步驟:
①創建Lock實體
Lock lock = new ReetrantLock();
②加鎖
lock.lock();
③釋放鎖
lock.unlock();
注意事項:
②一定要放在try外面,③一定要放在finally里面,
在java語言中,所有的鎖都默人實作方式為非公平鎖,
非公平鎖調度:當一個執行緒釋放鎖之后,剛好有另一個執行緒執行到獲取鎖的代碼,所以就直接獲取鎖,
公平鎖調度:一個執行緒釋放鎖,然后主動喚醒需要得到鎖的佇列來得到鎖,會有性能消耗,
所以非公平鎖的性能更高,
synchronized是非公平鎖,ReetrantLock也是非公平鎖,但是也可以顯式的宣告為公平鎖:
Lock lock = new ReetrantLock(true);
sychronized 和 Lock 的區別:
- 關鍵字不同;
- 前者自動加鎖和釋放鎖,后者自動加鎖和釋放鎖;
- Lock是java層面來實作,而sychronized是jvm層面來實作;
- 適用范圍不同:Lock只能修飾代碼塊,而sychronized可以修飾前面提到的三個場景;
- sychronized鎖的模式只有公平鎖模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/283171.html
標籤:其他
上一篇:對于volatile和synchronized的一些整理
下一篇:云服務器的使用和網站的搭建
