線程同步
-
現實生活中,我們會遇到“同一個資源,多個人都想使用”的問題,比如,食堂排隊打飯,每個人都想喫飯,最天然的解決辦法就是排隊,一個個來。
-
處理多線程問題時,多個線程訪問同一個對象,並且某些線程還想修改這個對象,這是我們就需要用到線程同步。線程同步其實就是一種等待機制,多個需要同時訪問此對象的線程進入這個對象的等待池
形成隊列,等待前面的線程使用完畢,下一個線程再使用。
形成條件
隊列+鎖
- 由於統一進程的多個線程共享同一塊存儲空間,在帶來方便的同時,也帶來了訪問衝突問題,爲了保證數據在方法中被訪問時的正確性,在訪問時加入
鎖機制synchronized
,當一個線程獲得對象的排它鎖,獨佔資源,其他線程必須等待,使用後釋放鎖即可,存在以下問題:
- 一個線程持有鎖會導致其他所有需要此鎖的線程掛起
- 在多線程競爭下,加鎖,釋放鎖會導致比較多的上下文切換和調度延遲,引起性能問題
- 如果一個優先級高的線程等待一個優先級低的線程會釋放鎖,會導致優先級倒置,引起性能問題
同步方法
- 由於我們可以通過private關鍵字來保證數據對象只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是synchronized關鍵字,它包括兩種用法:synchronized方法和synchronized塊
public synchronized void method(int args){}
- synchronized方法控制“對象”的訪問,每個對象對應一把鎖,每個synchronized方法都必須獲得調用該方法的對象的鎖才能執行,否則線程會阻塞,方法一旦執行,就獨佔該鎖,直到該方法返回才釋放鎖,後面被阻塞的線程才能獲得和這個鎖,繼續執行。缺陷:若將一個大的方法申明爲synchronized將會影響效率
同步塊
- 同步塊:synchronized(Obj){}
- Obj稱之爲同步監視器
- Obj可以是任何對象,但是推薦使用共享資源作爲同步監視器
- 同步方法中無需指定同步監視器,因爲同步方法的同步監視器就是this,就是這個對象本身,或者是class【反射】
- 同步監視器的執行過程
- 第一個線程訪問,鎖定同步監視器,執行其中代碼
- 第二個線程訪問,發現同步監視器被鎖定,無法訪問
- 第一個線程訪問完畢,解鎖同步監視器
- 第二個線程訪問,發現同步監視器沒有鎖,然後鎖定並訪問