synchronized作為內(nèi)置鎖,使用簡(jiǎn)單刁品,不易出錯(cuò)泣特,然鵝確有相當(dāng)?shù)木窒扌裕缣羲妫瑹o(wú)法從等待獲取鎖的阻塞中中斷状您,無(wú)法設(shè)置獲取鎖的超時(shí)。
所以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();
}
從接口的定義不難發(fā)現(xiàn),Lock不僅提供了常規(guī)的lock()阻塞式加鎖暑劝,也提供了tryLock使得線程能在獲取不到鎖時(shí)骆莹,馬上返回,
甚至可以等待鎖一段時(shí)間后担猛,再返回幕垦。lockInterruptibly則提供了可中斷的阻塞式獲取鎖方式。
Lock的鎖需要顯示釋放傅联,通常要與try...finally
語(yǔ)句一起使用先改,避免死鎖。
lock.lock();
try {
// update object state
// catch exceptions and restore invariants if necessary
} finally {
lock.unlock();
}
ReentrantLock
Lock最常用的實(shí)現(xiàn)類是ReentrantLock蒸走,這是一個(gè)可重入鎖(synchronized也是)仇奶。
ReentrantLock默認(rèn)和內(nèi)置鎖一樣,是非公平鎖比驻,但是支持公平鎖模式该溯,可以用ReentrantLock(true)
創(chuàng)建公平鎖岛抄。
可重入鎖
所謂可重入鎖,也就是說一個(gè)線程可以在持有該鎖的時(shí)候狈茉,再次獲取該鎖夫椭。可重入鎖通常與一個(gè)計(jì)數(shù)器關(guān)聯(lián)氯庆,第一次獲取鎖的時(shí)候蹭秋,計(jì)數(shù)器從0變?yōu)?,再次獲取鎖堤撵,變?yōu)?仁讨,以此類推。釋放鎖的時(shí)候实昨,計(jì)數(shù)器每次減1洞豁,直至減為0,該鎖才真正釋放給其他線程屠橄。
為啥需要可重入鎖
舉個(gè)例子(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();
}
}
子類覆蓋了父類方法族跛,并再次調(diào)用了父類的同步方法,如果鎖不支持重入锐墙,則會(huì)導(dǎo)致死鎖礁哄。
公平鎖
所謂公平鎖,其實(shí)就是指鎖的等待隊(duì)列執(zhí)行先進(jìn)先出溪北,等待最久的線程優(yōu)先獲得鎖桐绒。
但是內(nèi)置鎖和ReentrantLock默認(rèn)都是非公平的,為啥之拨?
因?yàn)榉枪芥i的性能更好茉继。一個(gè)事實(shí)是,一個(gè)線程從被喚醒到真正運(yùn)行中間有不可忽視的延遲蚀乔,這個(gè)延遲時(shí)間很可能長(zhǎng)到足夠一個(gè)運(yùn)行中的線程獲取鎖烁竭,并完成操作,然后釋放鎖吉挣。也即是說派撕,把鎖給’等待最久的線程‘的過程中,可以讓其他線程插隊(duì)獲取鎖睬魂,并歸還鎖终吼,還不會(huì)影響’等待最久的線程‘的運(yùn)行。這樣一來(lái)吞吐量就得到了提升氯哮。
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
本文代碼
轉(zhuǎn)載請(qǐng)注明原文地址:https://liam-blog.ml/2019/07/21/Scala-Concurrency-in-Practice-2/