天天看點

synchronized 和java.util.concurrent.locks.Lock的異同

Lock是Java 5以後引入的新的API,和關鍵字synchronized相比主要相同點:Lock 能完成synchronized所實作的所有功能;主要不同點:Lock有比synchronized更精确的線程語義和更好的性能,而且不強制性的要求一定要獲得鎖。synchronized會自動釋放鎖,而Lock一定要求程式員手工釋放,并且最好在finally 塊中釋放(這是釋放外部資源的最好的地方)。如果不釋放,可能會引起難以預料的後果(在多線程環境中)。

注意:lock 必須在 finally 塊中釋放。否則,如果受保護的代碼将抛出異常,鎖就有可能永遠得不到釋放

 1.當代碼塊 加上 synchrozized之後,代碼會發生什麼改變?

    答案:有兩條改變。一個是原子性(atomicity),一個是可見性(visibility)。原子性意味着一次隻能有一個線程獲得代碼鎖,進入synchronized 包圍的代碼塊中執行。而可見性則是對不同範圍内對變量的修改做出的一緻性。強調變量的可見性與一緻性,是因為在Java 記憶體中,記憶體緩存和編譯器優化在多線程條件下會造成各種反常行為。一般來說,線程以某種不必讓其他線程立即可以看到的方式(不管這些線程在寄存器(register)中、在處理器特定的緩存(CPU cache)中,還是通過指令重排或者其他編譯器優化),不受緩存變量值的限制,但是加人synchronized關鍵字之後,那麼運作庫将確定某一線程對變量所做的更新先于對現有 synchronized 塊所進行的更新,當進入由同一監控器(lock)保護的另一個 synchronized 塊時,将立刻可以看到這些對變量所做的更新。類似的規則也存在于 volatile 變量

  2. 為什麼需要java.util.concurrent.lock?

  主要是synchronized是比較古老的實作機制,設計較早,有一些功能上的限制:

—— 它無法中斷一個正在等候獲得鎖的線程

——也無法通過投票得到鎖,如果不想等下去,也就沒法得到鎖。

——同步還要求鎖的釋放隻能在與獲得鎖所在的堆棧幀相同的堆棧幀中進行

 3. 重入鎖 ReentrantLock

java.util.concurrent.lock 中的 Lock 架構是鎖定的一個抽象,它允許把鎖定的實作作為 Java 類,而不是作為語言的特性來實作。

這就為 Lock 的多種實作留下了空間,各種實作可能有不同的排程算法、性能特性或者鎖定語義。 

ReentrantLock 類實作了Lock ,它擁有與 synchronized 相同的并發性和記憶體語義,但是添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。

此外,它還提供了在激烈争用情況下更佳的性能。(換句話說,當許多線程都想通路共享資源時,JVM 可以花更少的時候來排程線程,把更多時間用在執行線程上。