synchronized做爲內置鎖,使用簡單,不易出錯,然鵝確有至關的侷限性,例如,沒法從等待獲取鎖的阻塞中中斷,沒法設置獲取鎖的超時。
因此JUC提供了另外一種更靈活的加鎖方式,即Lock。java
Lock接口定義以下git
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
從接口的定義不難發現,Lock不只提供了常規的lock()阻塞式加鎖,也提供了tryLock使得線程能在獲取不到鎖時,立刻返回,
甚至能夠等待鎖一段時間後,再返回。lockInterruptibly則提供了可中斷的阻塞式獲取鎖方式。github
Lock的鎖須要顯示釋放,一般要與try...finally
語句一塊兒使用,避免死鎖。ide
lock.lock(); try { // update object state // catch exceptions and restore invariants if necessary } finally { lock.unlock(); }
Lock最經常使用的實現類是ReentrantLock,這是一個可重入鎖(synchronized也是)。性能
ReentrantLock默認和內置鎖同樣,是非公平鎖,可是支持公平鎖模式,能夠用ReentrantLock(true)
建立公平鎖。線程
所謂可重入鎖,也就是說一個線程能夠在持有該鎖的時候,再次獲取該鎖。可重入鎖一般與一個計數器關聯,第一次獲取鎖的時候,計數器從0變爲1,再次獲取鎖,變爲2,以此類推。釋放鎖的時候,計數器每次減1,直至減爲0,該鎖才真正釋放給其餘線程。
爲啥須要可重入鎖
舉個例子(JCP書上的)scala
public class Widget { public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
子類覆蓋了父類方法,並再次調用了父類的同步方法,若是鎖不支持重入,則會致使死鎖。rest
所謂公平鎖,其實就是指鎖的等待隊列執行先進先出,等待最久的線程優先得到鎖。
可是內置鎖和ReentrantLock默認都是非公平的,爲啥?
由於非公平鎖的性能更好。一個事實是,一個線程從被喚醒到真正運行中間有不可忽視的延遲,這個延遲時間極可能長到足夠一個運行中的線程獲取鎖,並完成操做,而後釋放鎖。也便是說,把鎖給’等待最久的線程‘的過程當中,可讓其餘線程插隊獲取鎖,並歸還鎖,還不會影響’等待最久的線程‘的運行。這樣一來吞吐量就獲得了提高。code
package io.github.liam8.con import java.util.concurrent.TimeUnit import java.util.concurrent.locks.{Lock, ReentrantLock} object LockDemo { private val rtl: Lock = new ReentrantLock() var inc: Int = 0 def get(): Int = { rtl.lock() try { inc } finally { rtl.unlock() } } def addOne(): Unit = { rtl.lock() try { TimeUnit.SECONDS.sleep(1) inc = 1 + get() } finally { rtl.unlock() } } def main(args: Array[String]): Unit = { for (i <- 1 to 10) { new Thread { override def run(): Unit = { println(s"run thread $i") addOne() } }.start() } while (true) { println(s"inc=$inc") TimeUnit.SECONDS.sleep(1) } } }
outputblog
run thread 3 run thread 8 run thread 1 run thread 9 run thread 7 run thread 4 run thread 5 run thread 2 run thread 10 run thread 6 inc=0 inc=0 inc=2 inc=3 inc=4 inc=5 inc=6 inc=7 inc=8 inc=8 inc=10 inc=10 inc=10
轉載請註明原文地址:https://liam-blog.ml/2019/07/21/Scala-Concurrency-in-Practice-2/