淺談Java中的鎖機制介紹
淺談Java中的鎖
一、 悲觀鎖和樂觀鎖
悲觀鎖(Pessimistic Lock):顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。for update就是一種。
樂觀鎖(Optimistic Lock):顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。Java中常用的樂觀鎖方式有版本號、時間戳。
二、 公平鎖和非公平鎖
鎖的公平與非公平,是指執行緒請求獲取鎖的過程中,是否允許插隊。在公平鎖上,執行緒將按他們發出請求的順序來獲得鎖;而非公平鎖則允許線上程發出請求後立即嘗試獲取鎖,如果可用則可直接獲取鎖,嘗試失敗才進行排隊等待。
公平鎖:多個執行緒申請獲取同一個鎖,按照執行緒的申請順序,排隊獲取鎖。公平鎖的好處是等待的執行緒不會被餓死,相應的缺陷就是整體吞吐量很低、效率很低。使用new ReentrantLock(true)可以構造一個公平鎖。
非公平鎖:多個執行緒申請獲取同一個鎖,獲取鎖的順序不按照申請順序,搶佔式的獲取。非公平鎖的好處是整體效率很高,但是可能會使有些執行緒一致在等待,造成餓死。使用Synchronized、new ReentrantLock()和new ReentrantLock(false)可以構建一個非公平鎖。
三、 讀寫鎖
讀寫鎖一次只有一個執行緒(writer執行緒)可以修改共享資料,但在許多情況下,任何數量的執行緒可以同時讀取共享資料(reader執行緒)。Java中的讀寫鎖通過ReentrantReadWriteLock實現。ReentrantReadWriteLock.ReadLock是讀鎖,它是共享鎖。ReentrantReadWriteLock.WriteLock是寫鎖,它是獨佔鎖。
四、 互斥鎖
互斥鎖即一次只能有一個執行緒持有的鎖。ReentrantLock和synchronized都是互斥鎖。
ReentrantLock是jdk5的新特性,採用ReentrantLock可以完全替代替換synchronized傳統的鎖機制,而且採用ReentrantLock的方式更加面向物件,也更加靈活
五、 共享鎖和獨佔鎖
共享鎖:允許多個執行緒同時獲取鎖,併發訪問共享資源,如:ReadWriteLock。在實際使用過程中,執行緒A獲取到了共享資源D的共享鎖,其它執行緒只能獲取D的共享鎖,不能獲取獨佔鎖。
獨佔鎖:一次只能有一個執行緒獲得鎖,即只能被一個執行緒持有。在實際使用過程中,執行緒A獲取到了共享資源D的獨佔鎖,其它執行緒不能獲取D的任何型別鎖。(ReentrantLock就是以獨佔方式實現的互斥鎖。)
六、 偏向鎖、輕量級鎖、重量級鎖
這三個鎖都是針對synchronized來說的,具體的實現細節比較複雜,具體實現可檢視上面的阻塞鎖裡提到的。
偏向鎖:對於一段同步程式碼來說,鎖偏向於第一次獲取它的執行緒,如果繼續執行的過程中,鎖沒有被其它執行緒持有,則持有偏向鎖的執行緒將不需要同步,自動獲取鎖。加鎖和解鎖不需要額外的消耗,和執行非同步方法比僅存在納秒級的差距。如果執行緒間存在鎖競爭,會帶來額外的鎖撤銷的消耗。適用於只有一個執行緒訪問同步塊場景。
輕量級鎖:當偏向鎖被另一個執行緒持有的時候,偏向鎖升級為輕量級鎖,其它執行緒通過自旋轉的方式嘗試獲取鎖。競爭的執行緒不會阻塞,提高了程式的響應速度。如果始終得不到鎖競爭的執行緒使用自旋會消耗CPU。適用於追求響應時間、同步塊執行速度非常快。
重量級鎖:當輕量級鎖被另一個執行緒持有的時候,輕量級鎖升級為重量級鎖。執行緒競爭不使用自旋,不會消耗CPU。缺點就是執行緒阻塞,響應時間緩慢。適用於追求吞吐量,同步塊執行速度較長。
七、 自旋鎖
Java執行緒在得不到鎖時不會立即阻塞,而是執行一個迴圈,不斷的去嘗試獲取鎖,這種技術就是自旋鎖。它可以減少在獲取鎖的過程中,因為執行緒上下文的切換而導致的額外消耗。
八、 可重入鎖
可重入鎖,也稱為遞迴鎖,即執行緒在獲取到某方法的鎖之後,如果在該方法內部呼叫其它方法,這個方法也需要獲取鎖,那麼進入這個方法將自動獲取鎖,它可以在一定程度上避免死鎖。
九、 可中斷鎖
中斷(Interrupt)一個執行緒意味著在該執行緒完成任務之前停止其正在進行的一切,有效地中止其當前的操作。執行緒是死亡、還是等待新的任務或是繼續執行至下一步,就取決於這個程式。雖然初次看來它可能顯得簡單,但是,你必須進行一些預警以實現期望的結果。
從上面的介紹知道,interrupt()並不會使執行緒停止執行,那如何停止執行緒呢?
中斷執行緒最好的,最受推薦的方式是,使用共享變數(shared variable)發出訊號,告訴執行緒必須停止正在執行的任務。執行緒必須週期性的核查這一變數(尤其在冗餘操作期間),然後有秩序地中止任務。
ReentrantLock可中斷鎖的介紹。可中斷鎖是通過ReentrantLock提供的lockInterruptibly()方法實現的。