前言
線程同步機(jī)制是多線程下解決線程對(duì)共享資源競(jìng)爭(zhēng)的主要方式,華為倉(cāng)頡語(yǔ)言提供了三種常見(jiàn)的同步機(jī)制用來(lái)保證線程同步安全,分別是原子操作,互斥鎖和條件變量。本篇文章詳細(xì)介紹主要倉(cāng)頡語(yǔ)言解決同步機(jī)制的方法,建議點(diǎn)贊收藏!
同步機(jī)制
原子操作
和 java 一樣,倉(cāng)頡也支持使用原子操作(Atomic)用來(lái)確保多線程下的數(shù)據(jù)訪問(wèn)安全。主要是提供整數(shù)類(lèi)型,布爾類(lèi)型和引用類(lèi)型三種方式。
以整數(shù)類(lèi)型為例,原子變量 Atomic 包括 8 位(AtomicInt8) 至 64 位(AtomicInt64)的整數(shù)類(lèi)型,同時(shí)支持基本數(shù)據(jù)的讀寫(xiě)。
不使用原子操作,在多線程情況下對(duì)數(shù)據(jù)進(jìn)行累加。
? var sum: Int64 = 0
? for (pattern in 1..100) {
? ? ? ? spawn {
? ? ? ? ? sum += 1? ? ? ? ? ? ?
? ? ? ? ? ? }
? ? ? ? }
? sleep(Duration.second*2)
AppLog.info("Main===${sum}")
//輸出? Main===96
使用原子變量 AtomicInt64 對(duì)數(shù)據(jù)進(jìn)行累加。
? var sum = AtomicInt64(0)
? for (pattern in 1..100) {
? ? ? ? spawn {
? ? ? ? ? sum.fetchAdd(1)
? ? ? ? ? ? }
? ? ? ? }
? sleep(Duration.second*2)
? AppLog.info("Main===${sum.load()}")
//輸出? Main===99
原子操作 Atomic 使用compareAndSwap 用于數(shù)據(jù)的交換,使用的和 Java 中一樣的 CAS 同步機(jī)制用于確保在多線程的情況下能夠交換成功。
可重入互斥鎖
倉(cāng)頡語(yǔ)言中同樣支持使用可重入互斥鎖(ReentrantMutex)來(lái)解決多線程的同步問(wèn)題。當(dāng)一個(gè)線程獲取到共享變量的鎖時(shí),在該線程釋放鎖之前,其他線程都無(wú)法訪問(wèn)該共享變量,直到該線程持有的同步鎖釋放。
ReentrantMutex 可重入互斥鎖主要提供了三個(gè)方法 ,分別是 lock(),unlock(),tryLock()。lock()和 unlock()總是成對(duì)出現(xiàn)的,及對(duì)共享變量加完鎖后,等使用結(jié)束必須及時(shí)釋放鎖。
lock()和unlock()
還是以在多線程下對(duì)數(shù)據(jù)進(jìn)行累加操作。
? var sum = AtomicInt64(0)
? let mutex =? ReentrantMutex()
? for (pattern in 1..100) {
? ? ? ? spawn {
? ? ? ? ? ? mutex.lock()
? ? ? ? ? ? sum +=1
? ? ? ? ? ? mutex.unlock()
? ? ? ? ? ? }
? ? ? ? }
? sleep(Duration.second*2)
? AppLog.info("Main===${sum}")
//輸出? Main===99
ReentrantMutex 作為可重入互斥鎖,當(dāng)已經(jīng)獲取互斥鎖的線程再次獲取該互斥鎖時(shí),可以直接獲取。但是該線程獲取幾次互斥鎖就需要釋放幾次鎖。
tryLock()
tryLock 表示線程嘗試去獲取鎖,但是并一定能夠獲取到??梢酝ㄟ^(guò) tryLock()返回到布爾值判斷該線程釋放獲取到鎖,然后調(diào)用 unLock 釋放鎖。
Monitor
Monitor 是一個(gè)內(nèi)置鎖,繼承于ReentrantMutex 可重入互斥鎖。Monitor 不僅可以使用 lock(),unlock(),tryLock() 還提供了 wait(),notify(),notifyAll()三個(gè)方法用于解決線程間的數(shù)據(jù)安全問(wèn)題,這一點(diǎn)和 java 不同,java 中的 Object 提供了 wait(),notify(),notifyAll()三個(gè)方法。而 在倉(cāng)頡中是單獨(dú)封裝的類(lèi)。
下面舉例說(shuō)明:
let fun = spawn {
? ? ? ? ? ? ? ? monitor.lock()
? ? ? ? ? ? ? ? while (flag) {
? ? ? ? ? ? ? ? AppLog.info("Main=== thread 1 開(kāi)始執(zhí)行")
? ? ? ? ? ? ? ? monitor.wait()
? ? ? ? ? ? ? ? AppLog.info("Main=== thread 1 執(zhí)行結(jié)束")
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? monitor.unlock()
? ? ? ? ? ? ? ? }
? sleep(Duration.second)
? monitor.lock()
? AppLog.info("Main=== 主線程開(kāi)始執(zhí)行")
? flag = false? ? ? ? ? ? ? ? ? ?
? AppLog.info("Main===主線程執(zhí)行結(jié)束")? ? ? ? ? ? ? ? ? ?
? monitor.notifyAll()? ? ? ? ? ? ? ? ? ?
? monitor.unlock()? ? ? ? ? ? ? ? ? ?
? fun.get()? ? ?
//輸出
// Main=== thread 1 開(kāi)始執(zhí)行
// Main=== 主線程開(kāi)始執(zhí)行
// Main===主線程執(zhí)行結(jié)束
// Main=== thread 1 執(zhí)行結(jié)束
需要注意的是 monitor 的 wait 和 notify/notifyAll 方法使用之前必須要先獲取到鎖,即 lock()。
總結(jié)
倉(cāng)頡中的多線程并發(fā)安全同步機(jī)制十分重要,對(duì)于會(huì) Java 的小伙伴來(lái)說(shuō)簡(jiǎn)單容易上手,但是也有一些需要注意的點(diǎn),比如 notify 調(diào)用前必須要獲取到線程鎖,用完必須釋放否則會(huì)導(dǎo)致其他線程無(wú)法獲取到鎖,本篇文章就先講這些,已經(jīng)學(xué)會(huì)了的小伙伴,趕快動(dòng)手試試吧!。