Scala併發編程實戰 - 2:Lock 鎖 Lock Scala栗子 本文代碼

synchronized作爲內置鎖,使用簡單,不易出錯,然鵝確有相當的侷限性,例如,無法從等待獲取鎖的阻塞中中斷,無法設置獲取鎖的超時。
所以JUC提供了另一種更靈活的加鎖方式,即Lock。

Lock

Lock接口定義如下

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則提供了可中斷的阻塞式獲取鎖方式。

Lock的鎖需要顯示釋放,通常要與try...finally語句一起使用,避免死鎖。

lock.lock(); 
try {     
   // update object state     
   // catch exceptions and restore invariants if necessary 
} finally {     
   lock.unlock(); 
}

ReentrantLock

Lock最常用的實現類是ReentrantLock,這是一個可重入鎖(synchronized也是)。

ReentrantLock默認和內置鎖一樣,是非公平鎖,但是支持公平鎖模式,可以用ReentrantLock(true)創建公平鎖。

可重入鎖

所謂可重入鎖,也就是說一個線程可以在持有該鎖的時候,再次獲取該鎖。可重入鎖通常與一個計數器關聯,第一次獲取鎖的時候,計數器從0變爲1,再次獲取鎖,變爲2,以此類推。釋放鎖的時候,計數器每次減1,直至減爲0,該鎖才真正釋放給其他線程。
爲啥需要可重入鎖
舉個例子(JCP書上的)

public class Widget {     
    public synchronized void doSomething() {         
        ... 
    } 
}  
public class LoggingWidget extends Widget {     
    public synchronized void doSomething() {         
        System.out.println(toString() + ": calling doSomething");         
        super.doSomething();     
    } 
}

子類覆蓋了父類方法,並再次調用了父類的同步方法,如果鎖不支持重入,則會導致死鎖。

公平鎖

所謂公平鎖,其實就是指鎖的等待隊列執行先進先出,等待最久的線程優先獲得鎖。
但是內置鎖和ReentrantLock默認都是非公平的,爲啥?
因爲非公平鎖的性能更好。一個事實是,一個線程從被喚醒到真正運行中間有不可忽視的延遲,這個延遲時間很可能長到足夠一個運行中的線程獲取鎖,並完成操作,然後釋放鎖。也即是說,把鎖給’等待最久的線程‘的過程中,可以讓其他線程插隊獲取鎖,並歸還鎖,還不會影響’等待最久的線程‘的運行。這樣一來吞吐量就得到了提升。

Scala栗子

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)
    }
  }
}

output

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

本文代碼

Github倉庫

轉載請註明原文地址:https://liam-blog.ml/2019/07/21/Scala-Concurrency-in-Practice-2/

查看更多博主文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章