相關文章:
【多線程】一、線程狀態切換
【多線程】二、線程優先級&守護線程
【多線程】三、線程訪問變量
【多線程】四、線程異常抓捕
【多線程】五、線程池
【多線程】六、鎖與同步
【多線程】七、阻塞隊列
【多線程】八、異步計算結果獲取
【多線程】九、Android異步任務
鎖定的代碼塊在多線程訪問的時候以串行的方式執行,可以保證可見性、有序性、原子性
概念 | 含義 |
---|---|
可見性 | 每個線程有自己的內存緩存,其他線程緩存在本線程中不可見 |
原子性 | 對於非單一的指令,要麼都執行,要麼都不執行 |
有序性 | 編譯優化對指令進行重排序,只保證重排序的結果和沒排序的結果一致(happens-before規則) |
Lock和Condition
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 鎖測試,代替lock.lock()發生死鎖時可超時終止
// lockInterruptibly()無休止嘗試直到被interrupt();
if (lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
try {
if (不滿足業務條件) {
condition.await();
// 等待被喚醒: condition.signalAll();
}
// 獲取鎖成功, do ...
} finally {
lock.unlock();
}
} else { //
// 獲取鎖超時, do ...
}
同步修飾
synchornized
方法默認用當前對象
或class對象
作爲鎖;
synchornized
代碼塊需要顯示加對象鎖;
對象鎖只有一個默認條件(Object#wait()
,Object#notifyAll()
);
鎖相關
-
有鎖編程:從悲觀角度看待,認爲併發操作一定導致數據修改,必須加鎖保證數據的正確;
-
無鎖編程:從樂觀角度看待,認爲併發操作不一定導致數據修改,通常使用CAS算法實現;如
java.concurrent.atomic.*
, 但CAS可能會導致ABA問題,可用AtomicMarkableReference<T>
或AtomicStampedReference<T>
避免 -
分段設計: 一種鎖的設計思想;如
ConcurrentHashMap
中以每個Hash桶鏈表作爲鎖,put操作時對某個段加鎖,這樣實現了並行插入; -
自旋設計: 線程在獲取鎖的時候不立即阻塞而是通過忙循環嘗試,減少線程上下文切換的消耗。這一行爲會消耗CPU,基於JVM的線程調度可讓出忙循環時間片不會形成死鎖;
公平性 | 說明 |
---|---|
非公平 | 線程獲取鎖的順序不一定是申請順序,而爲保證最大吞吐量由系統自由調度; 可能會導致優先級反轉或飢餓現象 |
公平 | 線程獲取鎖的順序和申請的順序一致;ReentrantLock 底層通過AbstractQueueSynchronizer (AQS)實現調度,可指定爲公平策略;synchronized 修飾無法構造公平鎖; |
可重入性 | 說明 |
---|---|
可重入(遞歸鎖) | 外層方法獲取鎖以後進入內層方法可自動取得鎖;ReentrantLock 和synchronized 都是可重入鎖; |
不可重入 | 同一線程,內層方法不能取得外層方法的鎖,阻塞形成死鎖 |
共享性 | 說明 |
---|---|
獨享(互斥鎖) | 鎖在同一時刻只能被一個線程獲取;ReentrantLock 和synchronized 都是獨享鎖; |
共享(讀寫鎖) | 鎖在同一時刻可以被多個線程獲取;ReadWriteLock 讀鎖通過AQS實現讀取與讀取共享; |
鎖膨脹 | 說明 |
---|---|
偏向鎖 | 如果一個鎖(synchronized )一直被同一個線程訪問,那麼該線程可以自動獲取偏向它鎖而無需申請; |
輕量級鎖 | 若一個偏向鎖有了另一線程在申請,此時該鎖膨脹爲輕量級鎖,非偏向線程通過自旋獲取這個鎖 |
重量級鎖 | 若自旋一段時間還沒取得鎖,則線程休眠,鎖膨脹爲重量級鎖;重量級鎖在申請時會阻塞線程; |